blob: 7a53c1b7a379b1c588f35b8a2f0d0b12a5668f4b [file] [log] [blame]
/*
* 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.
*/
#ifndef __STACKMAP5_H__
#define __STACKMAP5_H__
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "../base/stackmap_x.h"
struct StackmapElement_5;
struct WorkmapElement_5;
//possible relations between verificaton types
enum ConstraintType {
CT_GENERIC = 0, // sub-defined type A is assignable to sub-defined type B
CT_ARRAY2REF = 1, // A is a known-type array. element of A is assignable to sub-defined type B
CT_EXPECTED_TYPE = 2, // sub-defined type A is assignable to known-type B
CT_INCOMING_VALUE = 3 // known-type A is assignable to sub-defined type B
};
//list constant verification type (i.e. known-type) that are assignable to some sub-definite type (i.e. StackMapElement)
//see StackmapElement
struct IncomingType {
//next in the list
IncomingType *nxt;
//value of the verification type recorded as int
_SmConstant value;
//simple next in the list
IncomingType *next() {
return nxt;
}
};
//list of constraints for some sub-definite verification type (i.e. StackMapElement)
//see StackmapElement
struct Constraint {
//next in the list
Constraint *nxt;
//either
union {
StackmapElement_5 *variable; // sub-definite verificarion type
int value; // or constant (known) verification type rcorded as int
};
//consatrint type
ConstraintType type;
//next constrait of type 't'
static Constraint *next(Constraint *cur, int t) {
while( cur && cur->type != t ) {
cur = (Constraint*)cur->next();
}
return cur;
}
//simple next in the list
Constraint *next() {
return nxt;
}
};
//constraint of the CT_EXPECTED_TYPE type: sub-defined type A is assignable to known-type B
struct ExpectedType : Constraint {
ExpectedType *next() {
return (ExpectedType *) Constraint::next(Constraint::next(), CT_EXPECTED_TYPE);
}
};
//constraint of the CT_GENERIC type: sub-defined type A is assignable to sub-defined type B
struct GenericCnstr : Constraint {
GenericCnstr *next() {
return (GenericCnstr *) Constraint::next(Constraint::next(), CT_GENERIC);
}
};
//constraint of the CT_ARRAY2REF type: A is a known-type array. element of A is assignable to sub-defined type B
struct ArrayCnstr : Constraint {
//there can be only one CT_ARRAY2REF per StackMap Element
ArrayCnstr *next() {
assert(0);
return 0;
}
};
//StackMapElement structure represens sub-definite verification type: we don't know what type is it, but
//we know about instructions that expect ExpectedTypes here and we know that IncomingValues can be here
//we also know that this type must be assignable to other sub-defenite types as indicated by CT_GENERIC
//constrains and there can be special limitations represented by CT_ARRAY2REF constraints
struct StackmapElement_5 { //TODO: should be rewritten to save footprint
//list of IncomingType constraint
IncomingType *incoming;
//list of all the conatraints of other types
Constraint *others;
//return value from any IncomingType constraint
//when we need to compae to some unmergable type we don;t need to interate thru the list
//also used to assert that an IncomingValue constraint exists
SmConstant getAnyIncomingValue() {
assert(firstIncoming());
return firstIncoming()->value;
}
//return first IncomingType constraint
IncomingType *firstIncoming() {
//TODO: I have to store somewhere the "modified" bit. Sorry.
return (IncomingType*)( (POINTER_SIZE_INT)incoming & ~3 );
}
//return first conatrint of any type except IncomingType
Constraint *firstOthers() {
return others;
}
//return first CT_EXPECTED_TYPE constraint
ExpectedType *firstExpected() {
return (ExpectedType*)Constraint::next(others, CT_EXPECTED_TYPE);
}
//return first CT_GENERIC constraint
GenericCnstr *firstGenericCnstr() {
return (GenericCnstr*)Constraint::next(others, CT_GENERIC);
}
//return first (and the only) CT_ARRAY2REF constraint
ArrayCnstr *firstArrayCnstr() {
return (ArrayCnstr*)Constraint::next(others, CT_ARRAY2REF);
}
//clean-up
void init() {
incoming = 0;
others = 0;
}
//add incoming type with the 'value' value
void newIncomingType(Memory *mem, SmConstant value) {
IncomingType *in = (IncomingType *)mem->malloc(sizeof(IncomingType));
POINTER_SIZE_INT mask = (POINTER_SIZE_INT)incoming & 3;
incoming = (IncomingType *) ((POINTER_SIZE_INT)incoming & ~3);
in->nxt = value == SM_BOGUS ? 0 : incoming;
//in->type = CT_INCOMING_VALUE;
in->value = value;
incoming = in;
incoming = (IncomingType *) ((POINTER_SIZE_INT)incoming | mask);
}
//add expected type with the 'value' value
void newExpectedType(Memory *mem, SmConstant value) {
newConstraint(mem, CT_EXPECTED_TYPE)->value = value.c;
}
Constraint *newConstraint(Memory *mem, int type) {
Constraint *o = (Constraint *)mem->malloc(sizeof(Constraint));
o->nxt = others;
o->type = (ConstraintType)type;
others = o;
return o;
}
//add generic constraint ('this' is assignable to 'to')
void newGenericConstraint(Memory *mem, StackmapElement_5 *to) {
newConstraint(mem, CT_GENERIC)->variable = to;
}
//add generic constraint ('this' is an array, which element is assignable to 'to')
void newArrayConversionConstraint(Memory *mem, StackmapElement_5 *to) {
assert(!firstArrayCnstr());
newConstraint(mem, CT_ARRAY2REF)->variable = to;
}
// return 'modified' flag for the stackmap. the flag is stored in the first bit of the 'incoming' pointer
// "modified" is about subroutines: you have to track which locals were changed
int isJsrModified() {
return (int)(POINTER_SIZE_INT)incoming & 1;
}
//set 'modified' flag for the stackmap. the flag is stored in the first bit of the 'incoming' pointer
// "modified" is about subroutines: you have to track which locals were changed
void setJsrModified() {
incoming = (IncomingType *) ((POINTER_SIZE_INT)incoming | 1);
}
//clear 'modified' flag for the stackmap. the flag is stored in the first bit of the 'incoming' pointer
// "modified" is about subroutines: you have to track which locals were changed
void clearJsrModified() {
incoming = (IncomingType *) ((POINTER_SIZE_INT)incoming & ~1);
}
};
//WorkMapElement structure represent an element of the workmap vector -- vector of the derived types
//a type might be either constant (or known) (e.g. if some previous instruction has put something on stack or locals)
//or sub-definite (e.g. if we've recently passed a branch target and don't know which types were on stack or locals)
struct WorkmapElement_5 {
//value. two low bits a used to store flags
union {
_SmConstant const_val; //either a constant (known-type)
StackmapElement_5 *var_ptr; //or a variable (sub-definite type)
};
//is it a sub-definite (not constant) type?
int isVariable() {
assert(const_val != SM_NONE);
return !((POINTER_SIZE_INT)var_ptr & 1);
}
//get value for the constant (known) verification type
SmConstant getConst() {
return const_val;
}
//get variable representing sub-definite verification type
StackmapElement_5 *getVariable() {
return (StackmapElement_5 *) ((POINTER_SIZE_INT)var_ptr & ~3);
}
//when we need to compae to some unmergable type we don;t need to interate thru the list
//also used to assert that an IncomingValue constraint exists
SmConstant getAnyPossibleValue() {
SmConstant ret = isVariable() ? getVariable()->getAnyIncomingValue() : (SmConstant) const_val;
assert(ret != SM_NONE);
return ret;
}
// return 'modified' flag for the workmap element. the flag is stored in the second bit of the union
//"modified" is about subroutines: you have to track which locals were changed
//it's easier to think of all the constants as "modified"
int isJsrModified() {
return (int)(POINTER_SIZE_INT)var_ptr & 3;
}
// set 'modified' flag for the workmap element. the flag is stored in the second bit of the union
void setJsrModified() {
if( isVariable() ) {
var_ptr = (StackmapElement_5*)((POINTER_SIZE_INT)var_ptr | 2);
}
}
};
//WorkmapElement type with some constructors
struct _WorkmapElement_5 : WorkmapElement_5 {
_WorkmapElement_5(WorkmapElement_5 other) {
const_val = other.const_val;
}
_WorkmapElement_5(StackmapElement_5 *s) {
var_ptr = s;
if( s->isJsrModified() ) {
setJsrModified();
}
}
_WorkmapElement_5(SmConstant c) {
const_val = c;
}
};
//possible flag value
static const short FF_ISWORKMAP = 1;
//structure for maintaining subroutine-specific data
//until subroutine is passed with the second (dataflow) pass we record to the wait list all JSR instructions
//calling this subroutine. Once the subroutine is over we continue 2nd pass for each wait-listed instruction
//see vf_Context_Base::SubroutineDone
struct SubroutineData {
Address caller; //first JSR instruction that called this subroutine
short retCount; //number of ret instructions for this subroutine
U_8 subrDataflowed; // =1 if dataflow pass for the subroutine is over
};
//Store various data for the given instruction. Possible data are: StackMap vector, WorkMap vector,
//Subroutine-specific data
//for a single instruction it might be either
// 1) no data
// 2) workmap only
// 3) stackmap only
// 4) stackmap and subroutine data. in this case two PropsHead structures are created the first one for the StackMap,
// it's 'next' points to the second PropsHead containing Subroutine info. In this case second PropsHead keeps 0xFFFF
// instead of 'instr'
// the list is used to organize storing Props as a HashTable
struct PropsHead_5 : public PropsHeadBase {
typedef MapHead<WorkmapElement_5> WorkmapHead;
typedef MapHead<StackmapElement_5> StackmapHead;
// really one bit is used: FF_ISWORKMAP. TODO: merge with (Stack|Work)map->flags
unsigned short instr_flags;
//actual properties
union {
WorkmapHead workmap;
StackmapHead stackmap;
};
//get workmap stored here
WorkmapHead *getWorkmap() {
assert(is_workmap());
return &workmap;
}
//get stackmap stored here
StackmapHead *getStackmap() {
assert(!is_workmap());
return &stackmap;
}
//get subroutine data stored here
SubroutineData *getSubrData(int el_cnt) {
assert(instr == 0xFFFF);
return (SubroutineData *) &stackmap.elements[el_cnt];
}
//is it a workmap?
int is_workmap() {
return instr_flags & FF_ISWORKMAP;
}
//set 'is workmap' flag
void set_as_workmap() {
instr_flags |= FF_ISWORKMAP;
}
//clear flag
void clearInstrFlag(short flag) {
instr_flags &= ~flag;
}
};
#endif