private static bool System_arraycopy(EmitIntrinsicContext eic) { // if the array arguments on the stack are of a known array type, we can redirect to an optimized version of arraycopy. // Note that we also have to handle VMSystem.arraycopy, because on GNU Classpath StringBuffer directly calls // this method to avoid prematurely initialising System. TypeWrapper dst_type = eic.GetStackTypeWrapper(0, 2); TypeWrapper src_type = eic.GetStackTypeWrapper(0, 4); if (!dst_type.IsUnloadable && dst_type.IsArray && dst_type == src_type) { switch (dst_type.Name[1]) { case 'J': case 'D': eic.Emitter.Emit(OpCodes.Call, ByteCodeHelperMethods.arraycopy_primitive_8); break; case 'I': case 'F': eic.Emitter.Emit(OpCodes.Call, ByteCodeHelperMethods.arraycopy_primitive_4); break; case 'S': case 'C': eic.Emitter.Emit(OpCodes.Call, ByteCodeHelperMethods.arraycopy_primitive_2); break; case 'B': case 'Z': eic.Emitter.Emit(OpCodes.Call, ByteCodeHelperMethods.arraycopy_primitive_1); break; default: // TODO once the verifier tracks actual types (i.e. it knows that // a particular reference is the result of a "new" opcode) we can // use the fast version if the exact destination type is known // (in that case the "dst_type == src_type" above should // be changed to "src_type.IsAssignableTo(dst_type)". TypeWrapper elemtw = dst_type.ElementTypeWrapper; // note that IsFinal returns true for array types, so we have to be careful! if (!elemtw.IsArray && elemtw.IsFinal) { eic.Emitter.Emit(OpCodes.Call, ByteCodeHelperMethods.arraycopy_fast); } else { eic.Emitter.Emit(OpCodes.Call, ByteCodeHelperMethods.arraycopy); } break; } return(true); } else { return(false); } }
private static bool Object_getClass(EmitIntrinsicContext eic) { // this is the null-check idiom that javac uses (both in its own source and in the code it generates) if (eic.MatchRange(0, 2) && eic.Match(1, NormalizedByteCode.__pop)) { eic.Emitter.Emit(OpCodes.Dup); eic.Emitter.EmitNullCheck(); return(true); } // this optimizes obj1.getClass() ==/!= obj2.getClass() else if (eic.MatchRange(0, 4) && eic.Match(1, NormalizedByteCode.__aload) && eic.Match(2, NormalizedByteCode.__invokevirtual) && (eic.Match(3, NormalizedByteCode.__if_acmpeq) || eic.Match(3, NormalizedByteCode.__if_acmpne)) && (IsSafeForGetClassOptimization(eic.GetStackTypeWrapper(0, 0)) || IsSafeForGetClassOptimization(eic.GetStackTypeWrapper(2, 0)))) { ClassFile.ConstantPoolItemMI cpi = eic.GetMethodref(2); if (cpi.Class == "java.lang.Object" && cpi.Name == "getClass" && cpi.Signature == "()Ljava.lang.Class;") { // we can't patch the current opcode, so we have to emit the first call to GetTypeHandle here eic.Emitter.Emit(OpCodes.Callvirt, Compiler.getTypeMethod); eic.PatchOpCode(2, NormalizedByteCode.__intrinsic_gettype); return(true); } } return(false); }
private static bool Unsafe_getObjectVolatile(EmitIntrinsicContext eic) { // the check here must be kept in sync with the hack in MethodAnalyzer.AnalyzeTypeFlow() TypeWrapper tw = eic.GetStackTypeWrapper(0, 1); if (IsSupportedArrayTypeForUnsafeOperation(tw)) { CodeEmitterLocal index = eic.Emitter.AllocTempLocal(Types.Int32); CodeEmitterLocal obj = eic.Emitter.AllocTempLocal(tw.TypeAsLocalOrStackType); eic.Emitter.Emit(OpCodes.Conv_Ovf_I4); eic.Emitter.Emit(OpCodes.Stloc, index); eic.Emitter.Emit(OpCodes.Stloc, obj); EmitConsumeUnsafe(eic); eic.Emitter.Emit(OpCodes.Ldloc, obj); eic.Emitter.Emit(OpCodes.Ldloc, index); eic.Emitter.ReleaseTempLocal(obj); eic.Emitter.ReleaseTempLocal(index); eic.Emitter.Emit(OpCodes.Ldelema, tw.TypeAsLocalOrStackType.GetElementType()); eic.Emitter.Emit(OpCodes.Volatile); eic.Emitter.Emit(OpCodes.Ldind_Ref); // remove the redundant checkcast that usually follows if (eic.Code[eic.OpcodeIndex + 1].NormalizedOpCode == NormalizedByteCode.__checkcast && tw.ElementTypeWrapper.IsAssignableTo(eic.ClassFile.GetConstantPoolClassType(eic.Code[eic.OpcodeIndex + 1].Arg1))) { eic.PatchOpCode(1, NormalizedByteCode.__nop); } eic.NonLeaf = false; return(true); } return(false); }
private static bool Object_getClass(EmitIntrinsicContext eic) { // this is the null-check idiom that javac uses (both in its own source and in the code it generates) if (eic.MatchRange(0, 2) && eic.Match(1, NormalizedByteCode.__pop)) { eic.Emitter.Emit(OpCodes.Dup); eic.Emitter.EmitNullCheck(); return(true); } // this optimizes obj1.getClass() ==/!= obj2.getClass() else if (eic.MatchRange(0, 4) && eic.Match(1, NormalizedByteCode.__aload) && eic.Match(2, NormalizedByteCode.__invokevirtual) && (eic.Match(3, NormalizedByteCode.__if_acmpeq) || eic.Match(3, NormalizedByteCode.__if_acmpne)) && (IsSafeForGetClassOptimization(eic.GetStackTypeWrapper(0, 0)) || IsSafeForGetClassOptimization(eic.GetStackTypeWrapper(2, 0)))) { ClassFile.ConstantPoolItemMI cpi = eic.GetMethodref(2); if (cpi.Class == "java.lang.Object" && cpi.Name == "getClass" && cpi.Signature == "()Ljava.lang.Class;") { // we can't patch the current opcode, so we have to emit the first call to GetTypeHandle here eic.Emitter.Emit(OpCodes.Callvirt, Compiler.getTypeMethod); eic.PatchOpCode(2, NormalizedByteCode.__intrinsic_gettype); return(true); } } // this optimizes obj.getClass() == Xxx.class else if (eic.MatchRange(0, 3) && eic.Match(1, NormalizedByteCode.__ldc) && eic.GetConstantType(1) == ClassFile.ConstantType.Class && (eic.Match(2, NormalizedByteCode.__if_acmpeq) || eic.Match(2, NormalizedByteCode.__if_acmpne))) { TypeWrapper tw = eic.GetClassLiteral(1); if (tw.IsGhost || tw.IsGhostArray || tw.IsUnloadable || (tw.IsRemapped && tw.IsFinal && tw is DotNetTypeWrapper)) { return(false); } eic.Emitter.Emit(OpCodes.Callvirt, Compiler.getTypeMethod); eic.Emitter.Emit(OpCodes.Ldtoken, (tw.IsRemapped && tw.IsFinal) ? tw.TypeAsTBD : tw.TypeAsBaseType); eic.Emitter.Emit(OpCodes.Call, Compiler.getTypeFromHandleMethod); eic.PatchOpCode(1, NormalizedByteCode.__nop); return(true); } return(false); }
private static bool Unsafe_compareAndSwapObject(EmitIntrinsicContext eic) { TypeWrapper tw = eic.GetStackTypeWrapper(0, 3); if (IsSupportedArrayTypeForUnsafeOperation(tw) && eic.GetStackTypeWrapper(0, 0).IsAssignableTo(tw.ElementTypeWrapper) && eic.GetStackTypeWrapper(0, 1).IsAssignableTo(tw.ElementTypeWrapper)) { Type type = tw.TypeAsLocalOrStackType.GetElementType(); CodeEmitterLocal update = eic.Emitter.AllocTempLocal(type); CodeEmitterLocal expect = eic.Emitter.AllocTempLocal(type); CodeEmitterLocal index = eic.Emitter.AllocTempLocal(Types.Int32); CodeEmitterLocal obj = eic.Emitter.AllocTempLocal(tw.TypeAsLocalOrStackType); eic.Emitter.Emit(OpCodes.Stloc, update); eic.Emitter.Emit(OpCodes.Stloc, expect); eic.Emitter.Emit(OpCodes.Conv_Ovf_I4); eic.Emitter.Emit(OpCodes.Stloc, index); eic.Emitter.Emit(OpCodes.Stloc, obj); EmitConsumeUnsafe(eic); eic.Emitter.Emit(OpCodes.Ldloc, obj); eic.Emitter.Emit(OpCodes.Ldloc, index); eic.Emitter.Emit(OpCodes.Ldelema, type); eic.Emitter.Emit(OpCodes.Ldloc, update); eic.Emitter.Emit(OpCodes.Ldloc, expect); eic.Emitter.Emit(OpCodes.Call, AtomicReferenceFieldUpdaterEmitter.MakeCompareExchange(type)); eic.Emitter.Emit(OpCodes.Ldloc, expect); eic.Emitter.Emit(OpCodes.Ceq); eic.Emitter.ReleaseTempLocal(obj); eic.Emitter.ReleaseTempLocal(index); eic.Emitter.ReleaseTempLocal(expect); eic.Emitter.ReleaseTempLocal(update); eic.NonLeaf = false; return(true); } if ((eic.Flags[eic.OpcodeIndex] & InstructionFlags.BranchTarget) != 0 || (eic.Flags[eic.OpcodeIndex - 1] & InstructionFlags.BranchTarget) != 0 || (eic.Flags[eic.OpcodeIndex - 2] & InstructionFlags.BranchTarget) != 0) { return(false); } if ((eic.Match(-1, NormalizedByteCode.__aload) || eic.Match(-1, NormalizedByteCode.__aconst_null)) && (eic.Match(-2, NormalizedByteCode.__aload) || eic.Match(-2, NormalizedByteCode.__aconst_null)) && eic.Match(-3, NormalizedByteCode.__getstatic)) { FieldWrapper fw = GetUnsafeField(eic, eic.GetFieldref(-3)); if (fw != null && fw.IsAccessibleFrom(fw.DeclaringType, eic.Caller.DeclaringType, fw.DeclaringType) && eic.GetStackTypeWrapper(0, 0).IsAssignableTo(fw.FieldTypeWrapper) && eic.GetStackTypeWrapper(0, 1).IsAssignableTo(fw.FieldTypeWrapper) && (fw.IsStatic || fw.DeclaringType == eic.GetStackTypeWrapper(0, 3))) { Type type = fw.FieldTypeWrapper.TypeAsLocalOrStackType; CodeEmitterLocal update = eic.Emitter.AllocTempLocal(type); CodeEmitterLocal expect = eic.Emitter.AllocTempLocal(type); eic.Emitter.Emit(OpCodes.Stloc, update); eic.Emitter.Emit(OpCodes.Stloc, expect); eic.Emitter.Emit(OpCodes.Pop); // discard index if (fw.IsStatic) { eic.Emitter.Emit(OpCodes.Pop); // discard obj EmitConsumeUnsafe(eic); eic.Emitter.Emit(OpCodes.Ldsflda, fw.GetField()); } else { CodeEmitterLocal obj = eic.Emitter.AllocTempLocal(eic.Caller.DeclaringType.TypeAsLocalOrStackType); eic.Emitter.Emit(OpCodes.Stloc, obj); EmitConsumeUnsafe(eic); eic.Emitter.Emit(OpCodes.Ldloc, obj); eic.Emitter.ReleaseTempLocal(obj); eic.Emitter.Emit(OpCodes.Ldflda, fw.GetField()); } eic.Emitter.Emit(OpCodes.Ldloc, update); eic.Emitter.Emit(OpCodes.Ldloc, expect); eic.Emitter.Emit(OpCodes.Call, AtomicReferenceFieldUpdaterEmitter.MakeCompareExchange(type)); eic.Emitter.Emit(OpCodes.Ldloc, expect); eic.Emitter.Emit(OpCodes.Ceq); eic.Emitter.ReleaseTempLocal(expect); eic.Emitter.ReleaseTempLocal(update); eic.NonLeaf = false; return(true); } } return(false); }
private static bool Unsafe_putObjectImpl(EmitIntrinsicContext eic, bool membarrier) { TypeWrapper tw = eic.GetStackTypeWrapper(0, 2); if (IsSupportedArrayTypeForUnsafeOperation(tw) && eic.GetStackTypeWrapper(0, 0).IsAssignableTo(tw.ElementTypeWrapper)) { CodeEmitterLocal value = eic.Emitter.AllocTempLocal(tw.ElementTypeWrapper.TypeAsLocalOrStackType); CodeEmitterLocal index = eic.Emitter.AllocTempLocal(Types.Int32); CodeEmitterLocal array = eic.Emitter.AllocTempLocal(tw.TypeAsLocalOrStackType); eic.Emitter.Emit(OpCodes.Stloc, value); eic.Emitter.Emit(OpCodes.Conv_Ovf_I4); eic.Emitter.Emit(OpCodes.Stloc, index); eic.Emitter.Emit(OpCodes.Stloc, array); EmitConsumeUnsafe(eic); eic.Emitter.Emit(OpCodes.Ldloc, array); eic.Emitter.Emit(OpCodes.Ldloc, index); eic.Emitter.Emit(OpCodes.Ldloc, value); eic.Emitter.ReleaseTempLocal(array); eic.Emitter.ReleaseTempLocal(index); eic.Emitter.ReleaseTempLocal(value); eic.Emitter.Emit(OpCodes.Stelem_Ref); if (membarrier) { eic.Emitter.EmitMemoryBarrier(); } eic.NonLeaf = false; return(true); } if ((eic.Flags[eic.OpcodeIndex] & InstructionFlags.BranchTarget) != 0 || (eic.Flags[eic.OpcodeIndex - 1] & InstructionFlags.BranchTarget) != 0) { return(false); } if ((eic.Match(-1, NormalizedByteCode.__aload) || eic.Match(-1, NormalizedByteCode.__aconst_null)) && eic.Match(-2, NormalizedByteCode.__getstatic)) { FieldWrapper fw = GetUnsafeField(eic, eic.GetFieldref(-2)); if (fw != null && (!fw.IsFinal || (!fw.IsStatic && eic.Caller.Name == "<init>") || (fw.IsStatic && eic.Caller.Name == "<clinit>")) && fw.IsAccessibleFrom(fw.DeclaringType, eic.Caller.DeclaringType, fw.DeclaringType) && eic.GetStackTypeWrapper(0, 0).IsAssignableTo(fw.FieldTypeWrapper) && (fw.IsStatic || fw.DeclaringType == eic.GetStackTypeWrapper(0, 2))) { CodeEmitterLocal value = eic.Emitter.AllocTempLocal(fw.FieldTypeWrapper.TypeAsLocalOrStackType); eic.Emitter.Emit(OpCodes.Stloc, value); eic.Emitter.Emit(OpCodes.Pop); // discard offset field if (fw.IsStatic) { eic.Emitter.Emit(OpCodes.Pop); // discard object EmitConsumeUnsafe(eic); } else { CodeEmitterLocal obj = eic.Emitter.AllocTempLocal(fw.DeclaringType.TypeAsLocalOrStackType); eic.Emitter.Emit(OpCodes.Stloc, obj); EmitConsumeUnsafe(eic); eic.Emitter.Emit(OpCodes.Ldloc, obj); eic.Emitter.ReleaseTempLocal(obj); } eic.Emitter.Emit(OpCodes.Ldloc, value); eic.Emitter.ReleaseTempLocal(value); // note that we assume the CLR memory model where all writes are ordered, // so we don't need a volatile store or a memory barrier and putOrderedObject // is typically used with a volatile field, so to avoid the memory barrier, // we don't use FieldWrapper.EmitSet(), but emit the store directly eic.Emitter.Emit(fw.IsStatic ? OpCodes.Stsfld : OpCodes.Stfld, fw.GetField()); if (membarrier) { eic.Emitter.EmitMemoryBarrier(); } eic.NonLeaf = false; return(true); } } return(false); }