All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robovm.compiler.StructMemberMethodCompiler Maven / Gradle / Ivy

/*
 * Copyright (C) 2012 RoboVM AB
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.robovm.compiler;


import static org.robovm.compiler.Bro.*;
import static org.robovm.compiler.Types.*;
import static org.robovm.compiler.llvm.Type.*;

import org.robovm.compiler.Bro.MarshalerFlags;
import org.robovm.compiler.clazz.Clazz;
import org.robovm.compiler.config.Config;
import org.robovm.compiler.llvm.BasicBlockRef;
import org.robovm.compiler.llvm.Bitcast;
import org.robovm.compiler.llvm.Function;
import org.robovm.compiler.llvm.Getelementptr;
import org.robovm.compiler.llvm.IntegerConstant;
import org.robovm.compiler.llvm.Inttoptr;
import org.robovm.compiler.llvm.Label;
import org.robovm.compiler.llvm.Load;
import org.robovm.compiler.llvm.PackedStructureType;
import org.robovm.compiler.llvm.PointerType;
import org.robovm.compiler.llvm.Ret;
import org.robovm.compiler.llvm.StructureType;
import org.robovm.compiler.llvm.Switch;
import org.robovm.compiler.llvm.Type;
import org.robovm.compiler.llvm.Unreachable;
import org.robovm.compiler.llvm.Value;
import org.robovm.compiler.llvm.Variable;
import org.robovm.compiler.llvm.VariableRef;

import org.robovm.compiler.llvm.VectorStructureType;
import soot.SootMethod;
import soot.VoidType;

import java.util.HashMap;
import java.util.Map;

/**
 * @author niklas
 *
 */
public class StructMemberMethodCompiler extends BroMethodCompiler {
    public static final String STRUCT_ATTRIBUTES_METHOD = "$attr$stretMetadata";
    private StructureType structType;

    public StructMemberMethodCompiler(Config config) {
        super(config);
    }

    @Override
    public void reset(Clazz clazz) {
        super.reset(clazz);
        structType = null;
        if (isStruct(sootClass)) {
            structType = getStructType(sootClass);
        }
    }
    
    @Override
    protected Function doCompile(ModuleBuilder moduleBuilder, SootMethod method) {
        if ("_sizeOf".equals(method.getName()) || "sizeOf".equals(method.getName())) {
            return structSizeOf(moduleBuilder, method);
        } else if ("offsetOf".equals(method.getName())) {
            return structOffsetOf(moduleBuilder, method);
        } else if (STRUCT_ATTRIBUTES_METHOD.equals(method.getName())) {
            return stretMeta(moduleBuilder, method);
        } else {
            return structMember(moduleBuilder, method);
        }
    }

    private Function structSizeOf(ModuleBuilder moduleBuilder, SootMethod method) {
        Function fn = createMethodFunction(method);
        moduleBuilder.addFunction(fn);
        fn.add(new Ret(sizeof(structType)));
        return fn;
    }

    private Function structOffsetOf(ModuleBuilder moduleBuilder, SootMethod method) {
        Function fn = createMethodFunction(method);
        moduleBuilder.addFunction(fn);

        int[] offsets = getStructMemberOffsets(structType);
        if (offsets.length > 0 ) {
            // function code -- basic switch of returns
            Label[] switchLabels = new Label[offsets.length];
            Map targets = new HashMap();
            for (int idx = 0; idx < offsets.length; idx++) {
                targets.put(new IntegerConstant(idx), fn.newBasicBlockRef(switchLabels[idx] = new Label(idx)));
            }

            Value idxValue = fn.getParameterRef(1); // 'env' is parameter 0
            Label def = new Label(-1);
            fn.add(new Switch(idxValue, fn.newBasicBlockRef(def), targets));

            // cases
            for (int idx = 0; idx < offsets.length; idx++) {
                fn.newBasicBlock(switchLabels[idx]);
                fn.add(new Ret(new IntegerConstant(offsets[idx])));
            }

            // default case -- array out of bounds exception
            fn.newBasicBlock(def);
        }
        Functions.call(fn, Functions.BC_THROW_ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION, fn.getParameterRef(0),
            new IntegerConstant(offsets.length), fn.getParameterRef(1));
        fn.add(new Unreachable());

        return fn;
    }

    private Function stretMeta(ModuleBuilder moduleBuilder, SootMethod method) {
        Function fn = createMethodFunction(method);
        moduleBuilder.addFunction(fn);
        fn.add(new Ret(new IntegerConstant(structType.getAttributes())));
        return fn;
    }

    private Function structMember(ModuleBuilder moduleBuilder, SootMethod method) {
        Function function = createMethodFunction(method);
        moduleBuilder.addFunction(function);
        
        // Get the value of the handle field in the Struct base class and cast it to a *
        Variable handleI64 = function.newVariable(I64);
        function.add(new Load(handleI64, getFieldPtr(function, function.getParameterRef(1), 
                offsetof(new StructureType(DATA_OBJECT, new StructureType(I64)), 1, 0), I64)));
        Variable handlePtr = function.newVariable(new PointerType(structType));
        function.add(new Inttoptr(handlePtr, handleI64.ref(), handlePtr.getType()));
        
        int offset = getStructMemberOffset(method) + structType.getOwnMembersOffset(); // Add offset since the first type in structType is the superclass type if any.
        Type memberType = getStructMemberType(method);
        Variable memberPtr = function.newVariable(new PointerType(memberType));
        // perform equals call instead of type comparision as there are multiple copies of struct types
        // being generated and this cause not required bitcast
        Type structMemberType = structType.getTypeAt(offset);
        if (!memberType.equals(structMemberType)) {
            // Several @StructMembers of different types have this offset (union or packed struct with allignment)
            Variable tmp = function.newVariable(new PointerType(structMemberType));
            if (structType instanceof PackedStructureType && structMemberType instanceof PackedStructureType) {
                // packed struct. member is wrapped into another packed struct to maintain required align, to get the value
                // it is required to provide extra index
                PackedStructureType packedMember = (PackedStructureType) structMemberType;
                if (packedMember.getTypeCount() != 2 || !packedMember.getTypeAt(0).equals(memberType)) {
                    throw new IllegalArgumentException("Internal error: method and struct member type missmatch. " + method);
                }
                function.add(new Getelementptr(tmp, handlePtr.ref(), 0, offset));
            } else if (structType instanceof VectorStructureType && ((VectorStructureType) structType).isVectorArray()) {
                // vector array struct represent following IR object
                // MatrixFloat2x2 for example { [2 x <2 x float>] }
                // to get to specific vector of <2 x float> it requires extra index, as
                // first 0 - points to first struct by pointer, e.g. ptr[0] and will return struct itself
                // second 0 - points to first member inside struct and will return array itself
                // third offset -- will return corresponding element of array
                function.add(new Getelementptr(tmp, handlePtr.ref(), 0, 0, offset));
            } else {
                function.add(new Getelementptr(tmp, handlePtr.ref(), 0, offset));
            }
            function.add(new Bitcast(memberPtr, tmp.ref(), memberPtr.getType()));
        } else {
            if (structType instanceof VectorStructureType && ((VectorStructureType) structType).isVectorArray()) {
                // check explanation above about extra 0
                function.add(new Getelementptr(memberPtr, handlePtr.ref(), 0, 0, offset));
            } else {
                function.add(new Getelementptr(memberPtr, handlePtr.ref(), 0, offset));
            }
        }
        
        VariableRef env = function.getParameterRef(0);
        if (method.getParameterCount() == 0) {
            // Getter
            Value result = loadValueForGetter(method, function, memberType, memberPtr.ref(),
                    function.getParameterRef(0), true, MarshalerFlags.CALL_TYPE_STRUCT_MEMBER);
            function.add(new Ret(result));
            
        } else {
            // Setter
            
            Value value = function.getParameterRef(2); // 'env' is parameter 0, 'this' is at 1, the value we're interested in is at index 2
            storeValueForSetter(method, function, memberType, memberPtr.ref(), env,
                    value, MarshalerFlags.CALL_TYPE_STRUCT_MEMBER);
            
            if (method.getReturnType().equals(VoidType.v())) {
                function.add(new Ret());
            } else {
                function.add(new Ret(function.getParameterRef(1)));
            }
        }
        
        return function;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy