Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch cir-stack-language Excluding Merge-Ins
This is equivalent to a diff from b8a2990900 to 9814f18b04
|
2022-06-29
| ||
| 21:47 | The CIR is no longer made out of instructions that are basically expression trees, but is now instead a stack language to make it possible to insert verification instructions before and after the evaluation of individual function arguments check-in: 1f87fbda15 user: zlodo tags: trunk | |
| 21:39 |
| |
|
2022-06-28
| ||
| 22:51 | Removed an unusued file and a left over debugging profanity check-in: 6c5b747f5c user: zlodo tags: cir-stack-language | |
|
2022-05-26
| ||
| 22:48 | Merge mac build fixes from trunk check-in: 536a94f712 user: zlodo tags: cir-stack-language | |
| 22:42 | Mac build fixes check-in: b8a2990900 user: zlodo tags: trunk | |
|
2022-05-15
| ||
| 19:25 | cir: implemented a helper object to decorate operations such as calls with a prologue and an epilogue, not used yet. check-in: 2503738366 user: zlodo tags: trunk | |
Changes to bs/builtins/builders/codebuilder.cpp.
| ︙ | ︙ | |||
119 120 121 122 123 124 125 |
auto result = InvokeOverloadSet( c, c.env()->extDestroyValue(),
MakeTuple( v ) );
if( result.isPoison() )
return false;
if( !result.isConstant() )
| | | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
auto result = InvokeOverloadSet( c, c.env()->extDestroyValue(),
MakeTuple( v ) );
if( result.isPoison() )
return false;
if( !result.isConstant() )
cfg()->currentBB()->append( result );
return true;
}
void CodeBuilder::extendValueLifetime( uint32_t index )
{
// Find the live value.
|
| ︙ | ︙ |
Changes to bs/builtins/exprhelpers.h.
1 2 3 4 5 6 7 8 9 10 11 |
#ifndef GOOSE_BUILTINS_EXPRHELPERS_H
#define GOOSE_BUILTINS_EXPRHELPERS_H
#include "parse/parse.h"
namespace goose::builtins::exprhelpers
{
template< typename V >
Value Not( V&& val )
{
return BuildComputedValue( GetValueType< bool >(),
| > | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
#ifndef GOOSE_BUILTINS_EXPRHELPERS_H
#define GOOSE_BUILTINS_EXPRHELPERS_H
#include "parse/parse.h"
namespace goose::builtins::exprhelpers
{
template< typename V >
Value Not( V&& val )
{
auto locId = val.locationId();
return BuildComputedValue( GetValueType< bool >(),
forward< V >( val ), cir::Not( locId ) ).setLocationId( locId );
}
template< typename L, typename R >
Value Or( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue( GetValueType< bool >(),
forward< L >( lhs ), forward< R >( rhs ), cir::Or( locId ) ).setLocationId( locId );
}
template< typename L, typename R >
Value And( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue( GetValueType< bool >(),
forward< L >( lhs ), forward< R >( rhs ), cir::And( locId ) ).setLocationId( locId );
}
template< typename L, typename R >
Value Eq( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue( GetValueType< bool >(),
forward< L >( lhs ), forward< R >( rhs ), cir::Eq( locId ) ).setLocationId( locId );
}
template< typename L, typename R >
Value Neq( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue( GetValueType< bool >(),
forward< L >( lhs ), forward< R >( rhs ), cir::Neq( locId ) ).setLocationId( locId );
}
template< typename L, typename R >
Value UGT( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue( GetValueType< bool >(),
forward< L >( lhs ), forward< R >( rhs ), cir::UGT( locId ) ).setLocationId( locId );
}
template< typename L, typename R >
Value UGE( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue( GetValueType< bool >(),
forward< L >( lhs ), forward< R >( rhs ), cir::UGE( locId ) ).setLocationId( locId );
}
template< typename L, typename R >
Value ULT( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue( GetValueType< bool >(),
forward< L >( lhs ), forward< R >( rhs ), cir::ULT( locId ) ).setLocationId( locId );
}
template< typename L, typename R >
Value ULE( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue( GetValueType< bool >(),
forward< L >( lhs ), forward< R >( rhs ), cir::ULE( locId ) ).setLocationId( locId );
}
template< typename L, typename R >
Value SGT( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue( GetValueType< bool >(),
forward< L >( lhs ), forward< R >( rhs ), cir::SGT( locId ) ).setLocationId( locId );
}
template< typename L, typename R >
Value SGE( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue( GetValueType< bool >(),
forward< L >( lhs ), forward< R >( rhs ), cir::SGE( locId ) ).setLocationId( locId );
}
template< typename L, typename R >
Value SLT( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue( GetValueType< bool >(),
forward< L >( lhs ), forward< R >( rhs ), cir::SLT( locId ) ).setLocationId( locId );
}
template< typename L, typename R >
Value SLE( L&& lhs, R&& rhs )
{
auto locId = max( lhs.locationId(), rhs.locationId() );
return BuildComputedValue( GetValueType< bool >(),
forward< L >( lhs ), forward< R >( rhs ), cir::SLE( locId ) ).setLocationId( locId );
}
}
#endif
|
Changes to bs/builtins/meson.build.
| ︙ | ︙ | |||
38 39 40 41 42 43 44 |
'types/func/bfunc.cpp',
'types/func/bintrinsic.cpp',
'types/func/functype.cpp',
'types/func/func.cpp',
'types/func/build.cpp',
'types/func/compile.cpp',
| < > > > > > > > > | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
'types/func/bfunc.cpp',
'types/func/bintrinsic.cpp',
'types/func/functype.cpp',
'types/func/func.cpp',
'types/func/build.cpp',
'types/func/compile.cpp',
'types/func/typecheck.cpp',
'types/func/lower.cpp',
'types/func/invocation/beagerfunc.cpp',
'types/func/invocation/bfunc.cpp',
'types/func/invocation/bintrinsic.cpp',
'types/func/invocation/common.cpp',
'types/func/invocation/func.cpp',
'types/func/invocation/ghostfunc.cpp',
'types/func/invocation/intrinsic.cpp',
'types/template/tvec.cpp',
'types/template/tvar.cpp',
'types/template/ttvar.cpp',
'types/template/tdecl.cpp',
'types/template/tnameddecl.cpp',
'types/template/tfunctype.cpp',
|
| ︙ | ︙ |
Changes to bs/builtins/operators/arith.cpp.
| ︙ | ︙ | |||
28 29 30 31 32 33 34 |
LeftAssInfixOp( "operator_add"_sid, precedence::AddSubOp,
BuildGenericTupleOperator(),
ForType< BigInt, Add >(),
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( lhs.type(),
| | | | | | | | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
LeftAssInfixOp( "operator_add"_sid, precedence::AddSubOp,
BuildGenericTupleOperator(),
ForType< BigInt, Add >(),
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( lhs.type(),
lhs, rhs, Add( c.locationId() ) );
} )
)
);
BuildParseRule( e, "-"_sid,
PrefixOp( "operator_unary_minus"_sid, precedence::UnaryOps,
BuildGenericTupleOperator(),
ForType< BigInt >( []( auto&& c, auto&& operand ) -> Value
{
return BuildComputedValue( GetValueType< BigInt >(),
ToValue( BigInt() ), operand, Sub( c.locationId() ) );
} ),
ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
[]( auto&& c, auto&& operand ) -> Value
{
auto opTypeVal = *EIRToValue( operand.type() );
auto opType = *FromValue< IntegerType >( opTypeVal );
return BuildComputedValue( operand.type(),
Value( operand.type(), APSInt( opType.m_numBits, false ) ),
operand, Sub( c.locationId() ) );
} )
),
LeftAssInfixOp( "operator_sub"_sid, precedence::AddSubOp,
BuildGenericTupleOperator(),
ForType< BigInt, Sub >(),
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( lhs.type(),
lhs, rhs, Sub( c.locationId() ) );
} )
)
);
BuildParseRule( e, "*"_sid,
LeftAssInfixOp( "operator_multiply"_sid, precedence::MulDivOp,
BuildGenericTupleOperator(),
ForType< BigInt, Mul >(),
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( lhs.type(),
lhs, rhs, Mul( c.locationId() ) );
} )
)
);
BuildParseRule( e, "/"_sid,
LeftAssInfixOp( "operator_divide"_sid, precedence::MulDivOp,
BuildGenericTupleOperator(),
|
| ︙ | ︙ | |||
96 97 98 99 100 101 102 |
// to construct the assertion expression.
auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
auto cond = Neq( rhs, zeroValue );
DiagnosticsManager::GetInstance().defineCustomDiagnostic(
cond.locationId(), "assert"_sid, "the divisor may be 0." );
| | | | | | | | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
// to construct the assertion expression.
auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
auto cond = Neq( rhs, zeroValue );
DiagnosticsManager::GetInstance().defineCustomDiagnostic(
cond.locationId(), "assert"_sid, "the divisor may be 0." );
cfg->currentBB()->append(
move( cond ), cir::Assert( rhs.locationId() )
);
return BuildComputedValue( lhs.type(),
lhs, rhs, SDiv( c.locationId() ) );
} ),
ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
using namespace goose::builtins::exprhelpers;
auto cfg = GetCFG( c );
assert( cfg );
// Build a zero constant of the same type as the denominator
// to construct the assertion expression.
auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
auto cond = Neq( rhs, zeroValue );
DiagnosticsManager::GetInstance().defineCustomDiagnostic(
cond.locationId(), "assert"_sid, "the divisor may be 0." );
cfg->currentBB()->append(
move( cond ), cir::Assert( rhs.locationId() )
);
return BuildComputedValue( lhs.type(),
lhs, rhs, UDiv( c.locationId() ) );
} )
)
);
BuildParseRule( e, "%"_sid,
LeftAssInfixOp( "operator_modulo"_sid, precedence::MulDivOp,
BuildGenericTupleOperator(),
|
| ︙ | ︙ | |||
149 150 151 152 153 154 155 |
// to construct the assertion expression.
auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
auto cond = Neq( rhs, zeroValue );
DiagnosticsManager::GetInstance().defineCustomDiagnostic(
cond.locationId(), "assert"_sid, "the divisor may be 0." );
| | | | | | | | 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
// to construct the assertion expression.
auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
auto cond = Neq( rhs, zeroValue );
DiagnosticsManager::GetInstance().defineCustomDiagnostic(
cond.locationId(), "assert"_sid, "the divisor may be 0." );
cfg->currentBB()->append(
move( cond ), cir::Assert( rhs.locationId() )
);
return BuildComputedValue( lhs.type(),
lhs, rhs, SRem( c.locationId() ) );
} ),
ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
using namespace goose::builtins::exprhelpers;
auto cfg = GetCFG( c );
assert( cfg );
// Build a zero constant of the same type as the denominator
// to construct the assertion expression.
auto zeroValue = Value( rhs.type(), APSInt::get( 0 ) );
auto cond = Neq( rhs, zeroValue );
DiagnosticsManager::GetInstance().defineCustomDiagnostic(
cond.locationId(), "assert"_sid, "the divisor may be 0." );
cfg->currentBB()->append(
move( cond ), cir::Assert( rhs.locationId() )
);
return BuildComputedValue( lhs.type(),
lhs, rhs, URem( c.locationId() ) );
} )
)
);
}
}
|
Changes to bs/builtins/operators/assignment.cpp.
| ︙ | ︙ | |||
28 29 30 31 32 33 34 |
auto cfg = GetCFG( c );
if( !cfg )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "assignments are not allowed here." );
return PoisonValue();
}
else if( auto bb = cfg->currentBB() )
| | | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
auto cfg = GetCFG( c );
if( !cfg )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( 0, "assignments are not allowed here." );
return PoisonValue();
}
else if( auto bb = cfg->currentBB() )
bb->append( lhs, rhs, Store( refType.type(), rhs.locationId(), lhs.locationId() ) );
// Return the ref to support chained assignments, like in c++.
return Value( lhs );
};
// Generic function to assign a value to a decl (local variable declaration)
auto DeclAssignment = []( auto&& c, auto&& lhs, auto&& rhs ) -> Value
|
| ︙ | ︙ | |||
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
return PoisonValue();
return result;
};
auto RTTupleAssignment = [assOp]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
auto tupSize = TupleSize( lhs );
auto tupRefType = *FromValue< ReferenceType >( *EIRToValue( rhs.type() ) );
auto rhsTupType = *EIRToValue( tupRefType.type() );
if( tupSize != TupleTypeSize( rhsTupType ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage( 0, "incompatible tuple sizes." );
return PoisonValue();
}
// Load the values from the rhs tuple into temporary vars.
vector< Value > tempVars;
tempVars.reserve( tupSize );
uint32_t index = 0;
bool success = true;
ForEachInTupleType( rhsTupType, [&]( auto&& srcType )
{
auto rt = ValueToEIR( ToValue( ReferenceType( srcType, ConstAccessSpecifer() ) ) );
| > > > > > > > > > > | | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
return PoisonValue();
return result;
};
auto RTTupleAssignment = [assOp]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
auto cfg = GetCFG( c );
if( !cfg )
return PoisonValue();
auto tupSize = TupleSize( lhs );
auto tupRefType = *FromValue< ReferenceType >( *EIRToValue( rhs.type() ) );
auto rhsTupType = *EIRToValue( tupRefType.type() );
if( tupSize != TupleTypeSize( rhsTupType ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage( 0, "incompatible tuple sizes." );
return PoisonValue();
}
// Store the source tuple as a temporary, otherwise we're going to recompute rhs
// for each member assigned (which is bad)
auto rhsIndex = cfg->getNewTemporaryIndex();
cfg->currentBB()->append( rhs, CreateTemporary( rhsIndex, rhs.locationId() ) );
auto srcTup = BuildComputedValue( rhs.type(), GetTemporary( rhs.type(), rhsIndex, rhs.locationId() ) );
// Load the values from the rhs tuple into temporary vars.
vector< Value > tempVars;
tempVars.reserve( tupSize );
uint32_t index = 0;
bool success = true;
ForEachInTupleType( rhsTupType, [&]( auto&& srcType )
{
auto rt = ValueToEIR( ToValue( ReferenceType( srcType, ConstAccessSpecifer() ) ) );
auto srcVal = BuildComputedValue( rt, srcTup, Select( index++, ( c.locationId() ) ) );
// We go at very high level to construct the temporary variables by resolving an invocation
// of the assignment of the rhs value to a TNamedDecl with an empty name.
// The reason we need to work at such high level is so that the initializer value gets
// resolved exactly like if it was a regular assignment. In particular, if the value in question
// is a locvar, we want it to go through the type checking process to be replaced with its content.
// This is the simplest and most robust way to achieve this, which should honor every relevant
|
| ︙ | ︙ |
Changes to bs/builtins/operators/comparison.cpp.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 |
ForType< BigInt, Eq, bool >(),
ForType< char32_t, Eq, bool >(),
ForType< string, Eq, bool >(),
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( GetValueType< bool >(),
| | | | | | | | | | | | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
ForType< BigInt, Eq, bool >(),
ForType< char32_t, Eq, bool >(),
ForType< string, Eq, bool >(),
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( GetValueType< bool >(),
lhs, rhs, Eq( c.locationId() ) );
} )
)
);
BuildParseRule( e, "!="_sid,
LeftAssInfixOp( "operator_not_equals"_sid, precedence::EqualityOp,
ForType< bool, Neq, bool >(),
ForType< BigInt, Neq, bool >(),
ForType< char32_t, Neq, bool >(),
ForType< string, Neq, bool >(),
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( GetValueType< bool >(),
lhs, rhs, Neq( c.locationId() ) );
} )
)
);
BuildParseRule( e, ">"_sid,
LeftAssInfixOp( "operator_greater"_sid, precedence::GreaterLesserOp,
ForType< BigInt, SGT, bool >(),
ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( GetValueType< bool >(),
lhs, rhs, SGT( c.locationId() ) );
} ),
ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( GetValueType< bool >(),
lhs, rhs, UGT( c.locationId() ) );
} )
)
);
BuildParseRule( e, ">="_sid,
LeftAssInfixOp( "operator_greater_or_equals"_sid, precedence::GreaterLesserOp,
ForType< BigInt, SGE, bool >(),
ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( GetValueType< bool >(),
lhs, rhs, SGE( c.locationId() ) );
} ),
ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( GetValueType< bool >(),
lhs, rhs, UGE( c.locationId() ) );
} )
)
);
BuildParseRule( e, "<"_sid,
LeftAssInfixOp( "operator_lesser"_sid, precedence::GreaterLesserOp,
ForType< BigInt, SLT, bool >(),
ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( GetValueType< bool >(),
lhs, rhs, SLT( c.locationId() ) );
} ),
ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( GetValueType< bool >(),
lhs, rhs, ULT( c.locationId() ) );
} )
)
);
BuildParseRule( e, "<="_sid,
LeftAssInfixOp( "operator_lesser_or_equals"_sid, precedence::GreaterLesserOp,
ForType< BigInt, SLE, bool >(),
ForType< CustomPattern< IntegerType, IntegerType::PatternSigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( GetValueType< bool >(),
lhs, rhs, SLE( c.locationId() ) );
} ),
ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( GetValueType< bool >(),
lhs, rhs, ULE( c.locationId() ) );
} )
)
);
}
}
|
Changes to bs/builtins/operators/dot.cpp.
| ︙ | ︙ | |||
36 37 38 39 40 41 42 |
if( index >= TupleTypeSize( tupType ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage( rhs.locationId(),
"the index is out of range." );
return PoisonValue();
}
| < | | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
if( index >= TupleTypeSize( tupType ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage( rhs.locationId(),
"the index is out of range." );
return PoisonValue();
}
auto rt = ValueToEIR( ToValue( ReferenceType( GetTupleTypeElement( tupType, index ), refType.behavior() ) ) );
return BuildComputedValue( rt, lhs, Select( index, c.locationId() ) );
} )
)
);
}
}
|
Changes to bs/builtins/operators/helpers.h.
| ︙ | ︙ | |||
72 73 74 75 76 77 78 |
auto ForType()
{
return []< typename tag >( auto&& e, auto&& pOvlSet, tag t )
{
using intrinsicType = Intrinsic< Value ( T, T ) >;
auto intrinsicFunc = []( const Context& c, const Value& lhs, const Value& rhs )
{
| | | | | | | 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
auto ForType()
{
return []< typename tag >( auto&& e, auto&& pOvlSet, tag t )
{
using intrinsicType = Intrinsic< Value ( T, T ) >;
auto intrinsicFunc = []( const Context& c, const Value& lhs, const Value& rhs )
{
return BuildComputedValue( GetValueType< RT >(), lhs, rhs, I( ( c.locationId() ) ) );
};
pOvlSet->add( e, ToValue< intrinsicType >( move( intrinsicFunc ) ), GetBuiltinIntrinsicFuncInvocationRule() );
};
}
template< typename T, typename F >
auto ForType( F&& func )
{
return [&]< typename tag >( auto&& e, auto&& pOvlSet, tag t )
{
if constexpr( is_same_v< tag, UnaryOpTag > )
{
using intrinsicType = Intrinsic< Value ( T ) >;
pOvlSet->add( e, ToValue< intrinsicType >( forward< F >( func ) ), GetBuiltinIntrinsicFuncInvocationRule() );
}
else
{
using intrinsicType = Intrinsic< Value ( T, T ) >;
pOvlSet->add( e, ToValue< intrinsicType >( forward< F >( func ) ), GetBuiltinIntrinsicFuncInvocationRule() );
}
};
}
template< typename T1, typename T2, typename F >
auto ForTypes( F&& func )
{
return [=]< typename tag >( auto&& e, auto&& pOvlSet, tag t )
{
using intrinsicType = Intrinsic< Value ( T1, T2 ) >;
pOvlSet->add( e, ToValue< intrinsicType >( func ), GetBuiltinIntrinsicFuncInvocationRule() );
};
}
}
#endif
|
Changes to bs/builtins/operators/logic.cpp.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 |
BuildParseRule( e, "!"_sid,
PrefixOp( "operator_logical_not"_sid, precedence::UnaryOps,
BuildGenericTupleOperator(),
ForType< bool >( []( auto&& c, auto&& operand ) -> Value
{
return BuildComputedValue( GetValueType< bool >(),
| | < | > | < | | | | | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
BuildParseRule( e, "!"_sid,
PrefixOp( "operator_logical_not"_sid, precedence::UnaryOps,
BuildGenericTupleOperator(),
ForType< bool >( []( auto&& c, auto&& operand ) -> Value
{
return BuildComputedValue( GetValueType< bool >(),
operand, cir::Not( c.locationId() ) );
} )
)
);
BuildParseRule( e, "~"_sid,
PrefixOp( "operator_bitwise_not"_sid, precedence::UnaryOps,
BuildGenericTupleOperator(),
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >( []( auto&& c, auto&& operand ) -> Value
{
auto opTypeVal = *EIRToValue( operand.type() );
auto opType = *FromValue< IntegerType >( opTypeVal );
return BuildComputedValue( operand.type(),
operand, Value( operand.type(), APSInt::getMaxValue( opType.m_numBits, true ) ),
Xor( c.locationId() )
);
} )
)
);
BuildParseRule( e, "^"_sid,
LeftAssInfixOp( "operator_xor"_sid, precedence::OrOp,
BuildGenericTupleOperator(),
// Logical xor
ForType< bool, Xor >(),
// ct_int xor
ForType< BigInt >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
if( !lhs.isConstant() || !rhs.isConstant() )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( c.locationId(), "bitwise operations between ct_int values are only allowed on constants." );
return PoisonValue();
}
return BuildComputedValue( lhs.type(),
lhs, rhs, Xor( c.locationId() ) );
} ),
// runtime integer xor, defined to work for any two integers of same
// bit size and signedness.
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( lhs.type(),
lhs, rhs, Xor( c.locationId() ) );
} )
)
);
BuildParseRule( e, "|"_sid,
LeftAssInfixOp( "operator_or"_sid, precedence::OrOp,
BuildGenericTupleOperator(),
// ct_int or
ForType< BigInt >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
if( !lhs.isConstant() || !rhs.isConstant() )
{
auto loc = Location::CreateSpanningLocation( lhs.locationId(), rhs.locationId() );
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( loc, "bitwise operations between ct_int values are only allowed on constants." );
return PoisonValue();
}
return BuildComputedValue( lhs.type(),
lhs, rhs, Or( c.locationId() ) );
} ),
// runtime integer or, defined to work for any two integers of same
// bit size and signedness.
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( lhs.type(),
lhs, rhs, Or( c.locationId() ) );
} ),
// bool or
ForType< bool >( [orOp]< typename L, typename R >( auto&& c, L&& lhs, R&& rhs ) -> Value
{
// Handle the case where lhs is constant, so that
// the result gets properly eagerly evaluated (in case
|
| ︙ | ︙ | |||
128 129 130 131 132 133 134 |
} )
)
);
RegisterBuiltinFunc< Intrinsic< bool ( Value, bool, bool ) > >( e, orOp,
[]( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
{
| | > | | | | | | < | | | | 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
} )
)
);
RegisterBuiltinFunc< Intrinsic< bool ( Value, bool, bool ) > >( e, orOp,
[]( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
{
return BuildComputedValue( GetValueType< bool >(), lhs, rhs, Or( c.locationId() ) );
} );
RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< CodeBuilder > >, bool, bool ) > >( e, orOp,
[]( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
{
auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( b );
const auto& cfg = cb->cfg();
// Build the control flow for shortcut evaluation.
const auto& predBB = cfg->currentBB();
auto pRhsBB = cfg->createBB();
auto pSuccBB = cfg->createBB();
// If the lhs is true, skip to the end directly.
// Otherwise, jump to the BB that computes rhs.
predBB->append( lhs );
predBB->setTerminator( CondBranch( pSuccBB, pRhsBB ) );
auto rhsIndex = cfg->getNewTemporaryIndex();
pRhsBB->append( rhs, CreateTemporary( rhsIndex, c.locationId() ) );
pRhsBB->setTerminator( Branch( pSuccBB ) );
auto resultIndex = cfg->getNewTemporaryIndex();
// Build the Phi instruction that will collect the final result.
auto phi = Phi( *EIRToValue( GetValueType< bool >() ), 2,
resultIndex, c.locationId() );
// If coming directly from the lhs BB, we know the result is true.
phi.setIncoming( predBB, ToValue( true ) );
// Otherwise, the result is whatever was computed by the rhs block.
phi.setIncoming( pRhsBB, BuildComputedValue( GetValueType< bool >(),
GetTemporary( GetValueType< bool >(), rhsIndex, c.locationId() ) ) );
pSuccBB->append( move( phi ) );
cfg->setCurrentBB( pSuccBB );
// Build the result val which pulls the temporary created above.
return BuildComputedValue( GetValueType< bool >(),
GetTemporary( GetValueType< bool >(), resultIndex, c.locationId() ) );
} );
BuildParseRule( e, "&"_sid,
LeftAssInfixOp( "operator_and"_sid, precedence::AndOp,
BuildGenericTupleOperator(),
// ct_int and
ForType< BigInt >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
if( !lhs.isConstant() || !rhs.isConstant() )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( c.locationId(), "bitwise operations between ct_int values are only allowed on constants." );
return PoisonValue();
}
return BuildComputedValue( lhs.type(),
lhs, rhs, And( c.locationId() ) );
} ),
// runtime integer and, defined to work for any two integers of same
// bit size and signedness.
ForType< CustomPattern< IntegerType, IntegerType::Pattern > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
return BuildComputedValue( lhs.type(),
lhs, rhs, And( c.locationId() ) );
} ),
// bool and
ForType< bool >( [andOp]< typename L, typename R >( auto&& c, L&& lhs, R&& rhs ) -> Value
{
// Handle the case where lhs is constant, so that
// the result gets properly eagerly evaluated (in case
|
| ︙ | ︙ | |||
228 229 230 231 232 233 234 |
} )
)
);
RegisterBuiltinFunc< Intrinsic< bool ( Value, bool, bool ) > >( e, andOp,
[]( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
{
| | > | | | | | | | | | | | | < | | | 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
} )
)
);
RegisterBuiltinFunc< Intrinsic< bool ( Value, bool, bool ) > >( e, andOp,
[]( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
{
return BuildComputedValue( GetValueType< bool >(), lhs, rhs, And( c.locationId() ) );
} );
RegisterBuiltinFunc< Intrinsic< bool ( TypeWrapper< ptr< CodeBuilder > >, bool, bool ) > >( e, andOp,
[]( auto&& c, auto&& b, auto&& lhs, auto&& rhs )
{
auto cb = *FromValue< TypeWrapper< ptr< CodeBuilder > > >( b );
const auto& cfg = cb->cfg();
// Build the control flow for shortcut evaluation.
const auto& predBB = cfg->currentBB();
auto pRhsBB = cfg->createBB();
auto pSuccBB = cfg->createBB();
// If the lhs is false, skip to the end directly.
// Otherwise, jump to the BB that computes rhs.
predBB->append( lhs );
predBB->setTerminator( CondBranch( pRhsBB, pSuccBB ) );
auto rhsIndex = cfg->getNewTemporaryIndex();
pRhsBB->append( rhs, CreateTemporary( rhsIndex, c.locationId() ) );
pRhsBB->setTerminator( Branch( pSuccBB ) );
auto resultIndex = cfg->getNewTemporaryIndex();
// Build the Phi instruction that will collect the final result.
auto phi = Phi( *EIRToValue( GetValueType< bool >() ), 2,
resultIndex, c.locationId() );
// If coming directly from the lhs BB, we know the result is false.
phi.setIncoming( predBB, ToValue( false ) );
// Otherwise, the result is whatever was computed by the rhs block.
phi.setIncoming( pRhsBB, BuildComputedValue( GetValueType< bool >(),
GetTemporary( GetValueType< bool >(), rhsIndex, c.locationId() ) ) );
pSuccBB->append( move( phi ) );
cfg->setCurrentBB( pSuccBB );
// Build the result val which pulls the temporary created above.
return BuildComputedValue( GetValueType< bool >(),
GetTemporary( GetValueType< bool >(), resultIndex, c.locationId() ) );
} );
BuildParseRule( e, "<<"_sid,
LeftAssInfixOp( "operator_shift_left"_sid, precedence::BitShiftOp,
BuildGenericTupleOperator(),
// ct_int left shift
ForTypes< BigInt, uint32_t >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
if( !lhs.isConstant() || !rhs.isConstant() )
{
auto loc = Location::CreateSpanningLocation( lhs.locationId(), rhs.locationId() );
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( loc, "bitwise operations between ct_int values are only allowed on constants." );
return PoisonValue();
}
return BuildComputedValue( lhs.type(),
lhs, rhs, Shl( c.locationId() ) );
} ),
// runtime integer left shift.
ForTypes< CustomPattern< IntegerType, IntegerType::Pattern >,
CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
using namespace goose::builtins::exprhelpers;
auto cfg = GetCFG( c );
assert( cfg );
// Shifting for a number of bits equal or larger than the bitsize
// of lhs is an undefined behavior, so we require verification that
// it won't happen.
// Extract the integer type of lhs to retrieve its bit size.
auto lhsType = *FromValue< IntegerType >( *EIRToValue( lhs.type() ) );
auto bitSizeValue = Value( rhs.type(), APSInt::get( lhsType.m_numBits ) );
auto cond = ULT( rhs, bitSizeValue );
DiagnosticsManager::GetInstance().defineCustomDiagnostic(
cond.locationId(), "assert"_sid, "the shift amount may be equal or greater than the bitsize." );
cfg->currentBB()->append(
move( cond ), cir::Assert( rhs.locationId() )
);
return BuildComputedValue( lhs.type(),
lhs, rhs, Shl( c.locationId() ) );
} )
)
);
BuildParseRule( e, ">>"_sid,
LeftAssInfixOp( "operator_shift_right"_sid, precedence::BitShiftOp,
BuildGenericTupleOperator(),
// ct_int right shift
ForTypes< BigInt, uint32_t >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
if( !lhs.isConstant() || !rhs.isConstant() )
{
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( c.locationId(), "bitwise operations between ct_int values are only allowed on constants." );
return PoisonValue();
}
return BuildComputedValue( lhs.type(),
lhs, rhs, AShr( c.locationId() ) );
} ),
// runtime signed integer right shift, defined to work for any two integers of same
// bit size.
ForTypes< CustomPattern< IntegerType, IntegerType::PatternSigned >,
CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
|
| ︙ | ︙ | |||
365 366 367 368 369 370 371 |
auto bitSizeValue = Value( rhs.type(), APSInt::get( lhsType.m_numBits) );
auto cond = ULT( rhs, bitSizeValue );
DiagnosticsManager::GetInstance().defineCustomDiagnostic(
cond.locationId(), "assert"_sid, "the shift amount may be equal or greater than the bitsize." );
| | | | | | | | | 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 |
auto bitSizeValue = Value( rhs.type(), APSInt::get( lhsType.m_numBits) );
auto cond = ULT( rhs, bitSizeValue );
DiagnosticsManager::GetInstance().defineCustomDiagnostic(
cond.locationId(), "assert"_sid, "the shift amount may be equal or greater than the bitsize." );
cfg->currentBB()->append(
move( cond ), cir::Assert( rhs.locationId() )
);
return BuildComputedValue( lhs.type(),
lhs, rhs, AShr( c.locationId() ) );
} ),
// runtime unsigned integer right shift, defined to work for any two integers of same
// bit size.
ForType< CustomPattern< IntegerType, IntegerType::PatternUnsigned > >(
[]( auto&& c, auto&& lhs, auto&& rhs ) -> Value
{
using namespace goose::builtins::exprhelpers;
auto cfg = GetCFG( c );
assert( cfg );
// Shifting for a number of bits equal or larger than the bitsize
// of lhs is an undefined behavior, so we require verification that
// it won't happen.
// Extract the integer type of lhs to retreieve its bit size.
auto lhsType = *FromValue< IntegerType >( *EIRToValue( lhs.type() ) );
auto bitSizeValue = Value( rhs.type(), APSInt::get( lhsType.m_numBits ) );
auto cond = ULT( rhs, bitSizeValue );
DiagnosticsManager::GetInstance().defineCustomDiagnostic(
cond.locationId(), "assert"_sid, "the shift amount may be equal or greater than the bitsize." );
cfg->currentBB()->append(
move( cond ), cir::Assert( rhs.locationId() )
);
return BuildComputedValue( lhs.type(),
lhs, rhs, LShr( c.locationId() ) );
} )
)
);
}
}
|
Changes to bs/builtins/operators/tuple.h.
| ︙ | ︙ | |||
23 24 25 26 27 28 29 |
result = AppendToTuple( result, r );
return true;
} );
return result;
};
| | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
result = AppendToTuple( result, r );
return true;
} );
return result;
};
pOvlSet->add( e, ToValue< intrinsicType >( move( intrinsicFunc ) ), GetBuiltinIntrinsicFuncInvocationRule() );
}
else
{
using intrinsicType = Intrinsic< Value ( CustomPattern< Value, TuplePattern >, CustomPattern< Value, TuplePattern > ) >;
auto intrinsicFunc = [pOvlSet]( const Context& c, const Value& lhs, const Value& rhs )
{
if( TupleSize( lhs ) != TupleSize( rhs ) )
|
| ︙ | ︙ | |||
54 55 56 57 58 59 60 |
result = AppendToTuple( result, r );
return true;
} );
return result;
};
| | | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
result = AppendToTuple( result, r );
return true;
} );
return result;
};
pOvlSet->add( e, ToValue< intrinsicType >( move( intrinsicFunc ) ), GetBuiltinIntrinsicFuncInvocationRule() );
}
};
}
}
#endif
|
Changes to bs/builtins/statements/if.cpp.
| ︙ | ︙ | |||
112 113 114 115 116 117 118 119 |
// If both the then and the else blocks successors exist and are terminated,
// we don't need a successor block.
if( !pElseBB || ( pThenSuccBB && !pThenSuccBB->terminator() )
|| ( pElseSuccBB && !pElseSuccBB->terminator() ) )
pSuccBB = cfg->createBB();
pPrecBB->setTerminator( cir::CondBranch(
| > < | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
// If both the then and the else blocks successors exist and are terminated,
// we don't need a successor block.
if( !pElseBB || ( pThenSuccBB && !pThenSuccBB->terminator() )
|| ( pElseSuccBB && !pElseSuccBB->terminator() ) )
pSuccBB = cfg->createBB();
pPrecBB->append( get< Value >( converted ) );
pPrecBB->setTerminator( cir::CondBranch(
pThenBB,
pElseBB ? pElseBB : pSuccBB ) );
if( pThenSuccBB && !pThenSuccBB->terminator() )
pThenSuccBB->setTerminator( cir::Branch( pSuccBB ) );
if( pElseSuccBB && !pElseSuccBB->terminator() )
|
| ︙ | ︙ |
Changes to bs/builtins/statements/return.cpp.
| ︙ | ︙ | |||
33 34 35 36 37 38 39 |
// Emit cleanups (destructor calls) for all currently live values in the function.
DestroyAllLiveValues( p.context() );
const auto& context = p.context();
if( context.returnType() == GetValueType< void >() )
{
| | > | > | > | | > | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
// Emit cleanups (destructor calls) for all currently live values in the function.
DestroyAllLiveValues( p.context() );
const auto& context = p.context();
if( context.returnType() == GetValueType< void >() )
{
cfg->emitTerminator( locationId, cir::RetVoid( locationId ) );
return true;
}
auto np = p.makeNestedParser();
if( !np.parseExpression( precedence::ReturnStmt + 1 ) )
{
dm.emitSyntaxErrorMessage( locationId, "expected an expression following the return statement.", 0 );
cfg->currentBB()->append( PoisonValue() );
cfg->emitTerminator( locationId, cir::Ret( locationId ) );
return false;
}
auto retVal = np.popValue();
if( !retVal )
{
dm.emitSyntaxErrorMessage( locationId, "expected an expression following the return statement.", 0 );
cfg->currentBB()->append( PoisonValue() );
cfg->emitTerminator( locationId, cir::Ret( locationId ) );
return false;
}
auto converted = ConvertValueToType( context, *retVal, *context.returnType() );
if( holds_alternative< ValUnifyError >( converted ) )
{
switch( get< ValUnifyError >( converted ) )
{
case ValUnifyError::NoSolution:
dm.emitErrorMessage( retVal->locationId(), "return value type mismatch." );
break;
case ValUnifyError::Ambiguous:
dm.emitErrorMessage( retVal->locationId(), "ambiguous return value conversion." );
break;
}
// Emit a terminator with a poison value to avoid the function compilation
// code to complain about a missing return.
cfg->currentBB()->append( PoisonValue() );
cfg->emitTerminator( locationId, cir::Ret( locationId ) );
PoisonBuilder( p.context() );
return true;
}
cfg->currentBB()->append( get< Value >( converted ) );
cfg->emitTerminator( locationId, cir::Ret( locationId ) );
return true;
};
RegisterRule( e, "return"_sid, Rule( handleReturn ) );
}
}
|
Changes to bs/builtins/statements/while.cpp.
| ︙ | ︙ | |||
128 129 130 131 132 133 134 135 |
if( pCont->level() == continueLevel )
bb->setTerminator( cir::Branch( pHeaderBB ) );
return;
}
} );
// Emit the conditional branch that will either run an iteration of the loop or exit to the succ bb.
pHeaderBB->setTerminator( cir::CondBranch(
| > < | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
if( pCont->level() == continueLevel )
bb->setTerminator( cir::Branch( pHeaderBB ) );
return;
}
} );
// Emit the conditional branch that will either run an iteration of the loop or exit to the succ bb.
pHeaderBB->append( get< Value >( converted ) );
pHeaderBB->setTerminator( cir::CondBranch(
pBodyBB, pSuccBB ) );
cfg->setCurrentBB( pSuccBB );
return true;
};
RegisterRule( e, "while"_sid, Rule( handleWhile ) );
|
| ︙ | ︙ |
Changes to bs/builtins/types/drop.cpp.
| ︙ | ︙ | |||
13 14 15 16 17 18 19 |
// is appended to the current BB of the current parser.
RegisterBuiltinFunc< Intrinsic< void ( Value, Value ) > >( e, e.extDropValue(),
[]( const Context& c, const Value& b, const Value& v )
{
if( v.isConstant() )
return;
| < < < | | | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
// is appended to the current BB of the current parser.
RegisterBuiltinFunc< Intrinsic< void ( Value, Value ) > >( e, e.extDropValue(),
[]( const Context& c, const Value& b, const Value& v )
{
if( v.isConstant() )
return;
if( !DoesInstrSeqHaveSideEffects( *v.cir() ) )
return;
auto cfg = GetCFG( c );
if( !cfg )
return;
auto bb = cfg->currentBB();
bb->append( move( *v.cir() ) );
} );
using AnyDeclType = CustomPattern< Decl, Decl::Pattern >;
// DropValue for Decls: declare a local variable with default initialization.
// TODO: if the invocation to InitializeValue fails, we should have a way to
// replace the generic "function arguments mismatch" error message with something
|
| ︙ | ︙ |
Changes to bs/builtins/types/func/bfunc.h.
1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_BUILTINS_TYPES_BFUNC_H
#define GOOSE_BUILTINS_TYPES_BFUNC_H
namespace goose::builtins
{
class FuncVerificationInfos;
using BuiltinFuncWrapper = function< Value ( const Term& argVec ) >;
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#ifndef GOOSE_BUILTINS_TYPES_BFUNC_H
#define GOOSE_BUILTINS_TYPES_BFUNC_H
namespace goose::builtins
{
class FuncVerificationInfos;
using BuiltinFuncWrapper = function< Value ( const Term& argVec ) >;
template< typename FT, typename F >
ptr< FuncVerificationInfos > RegisterBuiltinFunc( Env& env, StringId name, F&& func );
template< typename FT, typename F >
ptr< FuncVerificationInfos > RegisterBuiltinFunc( Env& env, ptr< OverloadSet > pOvlSet, F&& func );
extern bool IsBuiltinFunc( const Value& func );
extern bool IsEagerBuiltinFunc( const Value& func );
|
| ︙ | ︙ |
Changes to bs/builtins/types/func/bfunc.inl.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 |
}
template< typename FT, typename F >
ptr< FuncVerificationInfos > RegisterBuiltinFunc( Env& env, ptr< OverloadSet > pOvlSet, F&& func )
{
auto fvi = make_shared< builtins::FuncVerificationInfos >( RootG0Identity() );
| | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
}
template< typename FT, typename F >
ptr< FuncVerificationInfos > RegisterBuiltinFunc( Env& env, ptr< OverloadSet > pOvlSet, F&& func )
{
auto fvi = make_shared< builtins::FuncVerificationInfos >( RootG0Identity() );
if( !pOvlSet->add( env, ToValue< FT >( forward< F >( func ), fvi ), GetInvocationRule< FT >() ) )
G_ERROR( "duplicate overload registered for builtin func." );
return fvi;
}
}
namespace goose::eir
|
| ︙ | ︙ |
Changes to bs/builtins/types/func/build.cpp.
| ︙ | ︙ | |||
39 40 41 42 43 44 45 |
// Bind a stand-in value with the parameters name to be used inside of verification expressions.
auto paramVerificationIdentity = AppendToVectorTerm( verificationIdentity,
TERM( decl.name() ) );
c.env()->storeValue( paramVerificationIdentity, ANYTERM( _ ),
ValueToEIR( BuildComputedValue( decl.type(),
| | > | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
// Bind a stand-in value with the parameters name to be used inside of verification expressions.
auto paramVerificationIdentity = AppendToVectorTerm( verificationIdentity,
TERM( decl.name() ) );
c.env()->storeValue( paramVerificationIdentity, ANYTERM( _ ),
ValueToEIR( BuildComputedValue( decl.type(),
cir::VarAddr( varId++, decl.type(), param.locationId() ),
cir::Load( decl.type(), param.locationId() ) ) ) );
}
else if( param.isConstant() )
tv->append( ValueToEIR( param ) );
return true;
} );
if( failed )
return nullopt;
// If the return type is non-void, expose @result under the verification identity as a computed value whose type
// is the function's return type, and the value is a placeholder cir instruction. This will allow verification
// expressions to refer to the current function's return value as a value of the correct type.
auto rtTerm = ValueToEIR( returnType );
if( rtTerm != GetValueType< void >() )
{
auto name = "@result"_sid;
auto retValVerificationIdentity = AppendToVectorTerm( verificationIdentity, TERM( name ) );
c.env()->storeValue( retValVerificationIdentity, ANYTERM( _ ),
ValueToEIR( BuildComputedValue( rtTerm, cir::Placeholder( rtTerm, name, returnType.locationId() ) ) ) );
}
auto pVerifInfos = make_shared< FuncVerificationInfos >( move( verificationIdentity ) );
return FuncType( rtTerm, tv, move( pVerifInfos ) );
}
Func BuildExternalFunc( FuncType funcType, const string& symbol, bool varArg )
|
| ︙ | ︙ |
Changes to bs/builtins/types/func/compile.cpp.
| ︙ | ︙ | |||
194 195 196 197 198 199 200 |
// TODO: at some point we'll want to check for reachability in the static verifier,
// and either emit the implicit return or declare the code unreachable depending on the result.
// The reachability analysis will have to be done before contract validation, as the
// calls to DestroyValue() may also have requirements to enforce, so we'll need to emit
// the eventual implicit return first.
p.flushValue();
cb->destroyAllLiveValues( localContext );
| | | 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
// TODO: at some point we'll want to check for reachability in the static verifier,
// and either emit the implicit return or declare the code unreachable depending on the result.
// The reachability analysis will have to be done before contract validation, as the
// calls to DestroyValue() may also have requirements to enforce, so we'll need to emit
// the eventual implicit return first.
p.flushValue();
cb->destroyAllLiveValues( localContext );
cfg->emitTerminator( r->currentLocation(), cir::RetVoid( r->currentLocation() ) );
}
pFuncCIR->body() = cfg;
verify::Func fv( localContext, f );
return fv.verify();
}
}
|
Changes to bs/builtins/types/func/func.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include "builtins/builtins.h"
#include "lex/lex.h"
#include "parse/parse.h"
#include "verify/verify.h"
using namespace goose::builtins;
using namespace goose::parse;
namespace goose::builtins
{
const Term& FuncPattern::GetPattern()
{
static auto pattern = ValueToEIR(
Value( TypeType(), VEC( TSID( func ), HOLE( "llvmType"_sid ),
HOLE( "_"_sid ), HOLE( "_"_sid ),
HOLE( "_"_sid ), HOLE( "_"_sid ) ) ) );
| > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#include "builtins/builtins.h"
#include "lex/lex.h"
#include "parse/parse.h"
#include "verify/verify.h"
using namespace goose::builtins;
using namespace goose::parse;
namespace goose::builtins
{
cir::InstrSeq BuildArgsInstrSeq( const Term& args )
{
cir::InstrSeq result;
ForEachInVectorTerm( args, [&]( auto&& arg )
{
cir::AppendToInstrSeq( result, *EIRToValue( arg ) );
return true;
} );
return result;
}
const Term& FuncPattern::GetPattern()
{
static auto pattern = ValueToEIR(
Value( TypeType(), VEC( TSID( func ), HOLE( "llvmType"_sid ),
HOLE( "_"_sid ), HOLE( "_"_sid ),
HOLE( "_"_sid ), HOLE( "_"_sid ) ) ) );
|
| ︙ | ︙ | |||
52 53 54 55 56 57 58 |
Term GetFuncSig( const Value& func )
{
auto funcType = EIRToValue( func.type() );
assert( funcType );
return GetFuncSigFromType( *funcType );
}
| | | > | > | < > > | 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
Term GetFuncSig( const Value& func )
{
auto funcType = EIRToValue( func.type() );
assert( funcType );
return GetFuncSigFromType( *funcType );
}
optional< Term > GetFuncRType( const Value& func )
{
auto funcType = EIRToValue( func.type() );
if( !funcType )
return nullopt;
auto typeDecomp = Decompose( funcType->val(),
Vec(
Lit( "func"_sid ),
SubTerm(), // kind
SubTerm(), // return type
SubTerm(), // param types
SubTerm(), // verif infos
SubTerm() // varArg
)
);
if( !typeDecomp )
return nullopt;
auto&& [kind, rtype, ptypes, vinf, varArg] = *typeDecomp;
return rtype;
}
const cir::Func* GetFuncCIR( const Value& f )
{
if( !f.isConstant() )
return nullptr;
|
| ︙ | ︙ |
Changes to bs/builtins/types/func/func.h.
1 2 3 4 5 |
#ifndef GOOSE_BUILTINS_TYPES_FUNC_H
#define GOOSE_BUILTINS_TYPES_FUNC_H
namespace goose::builtins
{
| < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#ifndef GOOSE_BUILTINS_TYPES_FUNC_H
#define GOOSE_BUILTINS_TYPES_FUNC_H
namespace goose::builtins
{
extern void SetupFunctionTypeChecking( Env& e );
extern void SetupFunctionLowering( Env& e );
// Helper to provide generic param patterns for functions.
struct FuncPattern
{
static const Term& GetPattern();
};
extern Term GetFuncSig( const Value& func );
extern optional< Term > GetFuncRType( const Value& func );
extern const cir::Func* GetFuncCIR( const Value& func );
template< typename F >
void ForEachDeclInTuple( const Value& tup, F&& func );
extern bool IsExternalFunc( const Value& f );
extern bool IsIntrinsicFunc( const Value& f );
|
| ︙ | ︙ | |||
81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
};
extern Value CompileFunc( const Context& c, const Value& f );
extern bool ParseFuncConstraints( const Context& c, const FuncType& funcType );
extern bool ParseFuncBody( const Context& c, const Value& f );
extern bool ParseFuncBody( const Context& c, const Func& f );
}
namespace goose::eir
{
template<>
struct Bridge< builtins::Func >
{
| > > | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
};
extern Value CompileFunc( const Context& c, const Value& f );
extern bool ParseFuncConstraints( const Context& c, const FuncType& funcType );
extern bool ParseFuncBody( const Context& c, const Value& f );
extern bool ParseFuncBody( const Context& c, const Func& f );
extern cir::InstrSeq BuildArgsInstrSeq( const Term& args );
}
namespace goose::eir
{
template<>
struct Bridge< builtins::Func >
{
|
| ︙ | ︙ |
Changes to bs/builtins/types/func/functype.cpp.
| ︙ | ︙ | |||
36 37 38 39 40 41 42 |
auto result = Decompose( t.val(),
Vec(
Lit( "func"_sid ),
Lit( "intrinsic"_sid ),
SubTerm(), // return type
SubTerm(), // param types
SubTerm(), // verif infos
| | | | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
auto result = Decompose( t.val(),
Vec(
Lit( "func"_sid ),
Lit( "intrinsic"_sid ),
SubTerm(), // return type
SubTerm(), // param types
SubTerm(), // verif infos
SubTerm() // varArg
)
);
return !!result;
}
Term GetFuncSigFromType( const Value& funcType )
{
auto typeDecomp = Decompose( funcType.val(),
Vec(
Lit( "func"_sid ),
SubTerm(), // kind
SubTerm(), // return type
SubTerm(), // param types
SubTerm(), // verif infos
SubTerm() // varArg
)
);
assert( typeDecomp );
auto&& [kind, rtype, ptypes, vinf, varArg] = *typeDecomp;
return PrependToVectorTerm( *Unquote( ptypes ), rtype );
}
|
| ︙ | ︙ | |||
95 96 97 98 99 100 101 102 |
bool failed = false;
ForEachInVectorTerms( ft.params(), unifiedArgs, [&]( auto&& p, auto&& a )
{
auto vp = ValuePatternFromEIR( p );
if( vp->val() == HOLE( "_"_sid ) )
{
auto convertedArg = InvokeOverloadSet( c, c.env()->extConvertFuncArg(),
| > | > | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
bool failed = false;
ForEachInVectorTerms( ft.params(), unifiedArgs, [&]( auto&& p, auto&& a )
{
auto vp = ValuePatternFromEIR( p );
if( vp->val() == HOLE( "_"_sid ) )
{
auto arg = *EIRToValue( a );
auto convertedArg = InvokeOverloadSet( c, c.env()->extConvertFuncArg(),
MakeTuple( arg, *EIRToValue( vp->type() ) ) );
if( convertedArg.isPoison() )
{
failed = true;
return false;
}
convertedArg.setLocationId( arg.locationId() );
av->append( ValueToEIR( convertedArg ) );
}
return true;
} );
if( failed )
|
| ︙ | ︙ |
Added bs/builtins/types/func/invocation/beagerfunc.cpp.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class BuiltinEagerFuncInvocationRule : public BaseFuncInvocationRule
{
public:
Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
auto callDecomp = Decompose( typeCheckedCallPat,
Val< pvec >()
);
const auto& typeCheckedRType = callDecomp->get()->terms().front();
auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
auto argCount = VecSize( typeCheckedArgs );
auto argsInstrSeq = BuildArgsInstrSeq( typeCheckedArgs );
auto call = BuildComputedValue( typeCheckedRType, argsInstrSeq, callee,
cir::Call( argCount, loc ) );
if( !cir::CanValueBeEagerlyEvaluated( call ) )
return call;
execute::VM vm;
return execute::Evaluate( call, vm );
}
Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
return callee;
}
};
const ptr< InvocationRule >& GetBuiltinEagerFuncInvocationRule()
{
static ptr< InvocationRule > pRule = make_shared< BuiltinEagerFuncInvocationRule >();
return pRule;
}
void SetupBuiltinEagerFuncInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule(
ValueToEIR( ValuePattern( ANYTERM( _ ),
ValueToEIR( Value( TypeType(), VEC( TSID( func ),
TSID( builtin_eager ),
ANYTERM( _ ), ANYTERM( _ ),
ANYTERM( _ ), ANYTERM( _ ) ) ) ),
ANYTERM( _ ) ) ),
GetBuiltinEagerFuncInvocationRule() );
}
}
|
Added bs/builtins/types/func/invocation/bfunc.cpp.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class BuiltinFuncInvocationRule : public BaseFuncInvocationRule
{
public:
Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
auto callDecomp = Decompose( typeCheckedCallPat,
Val< pvec >()
);
const auto& typeCheckedRType = callDecomp->get()->terms().front();
auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
auto argCount = VecSize( typeCheckedArgs );
auto argsInstrSeq = BuildArgsInstrSeq( typeCheckedArgs );
return BuildComputedValue( typeCheckedRType, argsInstrSeq, callee, cir::Call( argCount, loc ) );
}
Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
return callee;
}
};
const ptr< InvocationRule >& GetBuiltinFuncInvocationRule()
{
static ptr< InvocationRule > pRule = make_shared< BuiltinFuncInvocationRule >();
return pRule;
}
void SetupBuiltinFuncInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule(
ValueToEIR( ValuePattern( ANYTERM( _ ),
ValueToEIR( Value( TypeType(), VEC( TSID( func ),
TSID( builtin ),
ANYTERM( _ ), ANYTERM( _ ),
ANYTERM( _ ), ANYTERM( _ ) ) ) ),
ANYTERM( _ ) ) ),
GetBuiltinFuncInvocationRule() );
}
}
|
Added bs/builtins/types/func/invocation/bintrinsic.cpp.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class BuiltinIntrinsicFuncInvocationRule : public BaseFuncInvocationRule
{
public:
Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
return GetBuiltinIntrinsicFuncWrapper( callee )( c, move( typeCheckedArgs ) );
}
Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
return callee;
}
};
const ptr< InvocationRule >& GetBuiltinIntrinsicFuncInvocationRule()
{
static ptr< InvocationRule > pRule = make_shared< BuiltinIntrinsicFuncInvocationRule >();
return pRule;
}
void SetupBuiltinIntrinsicFuncInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule(
ValueToEIR( ValuePattern( ANYTERM( _ ),
ValueToEIR( Value( TypeType(), VEC( TSID( func ),
TSID( intrinsic_builtin ),
ANYTERM( _ ), ANYTERM( _ ),
ANYTERM( _ ), ANYTERM( _ ) ) ) ),
ANYTERM( _ ) ) ),
GetBuiltinIntrinsicFuncInvocationRule() );
}
}
|
Added bs/builtins/types/func/invocation/common.cpp.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
Value BaseFuncInvocationRule::resolveInvocation( Context& c, LocationId loc, const Value& callee, const Term& args ) const
{
optional< TypeCheckingContext > bestTCC;
optional< Term > bestSol;
auto sig = GetFuncSig( callee );
auto callPat = PrependToVectorTerm( args, HOLE( "_"_sid ) );
auto us = FindBestTyping( sig, callPat, c );
if( holds_alternative< NoUnification >( us ) )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"function arguments mismatch." );
return PoisonValue();
}
if( holds_alternative< AmbiguousTypeCheck >( us ) )
{
// TODO display details
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"ambiguous function call." );
return PoisonValue();
}
auto&& [s,tcc] = get< TCSol >( us );
return invoke( c, loc, callee, args, s, tcc );
}
optional< Term > BaseFuncInvocationRule::getSignature( const Value& callee ) const
{
return GetFuncSig( callee );
}
}
|
Added bs/builtins/types/func/invocation/common.h.
> > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#ifndef GOOSE_BUILTINS_TYPES_FUNC_INVOCATION_COMMON_H
#define GOOSE_BUILTINS_TYPES_FUNC_INVOCATION_COMMON_H
namespace goose::builtins
{
class BaseFuncInvocationRule : public InvocationRule
{
public:
Value resolveInvocation( Context& c, LocationId loc, const Value& callee, const Term& args ) const final;
optional< Term > getSignature( const Value& callee ) const final;
};
}
#endif
|
Added bs/builtins/types/func/invocation/func.cpp.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class FuncInvocationRule : public BaseFuncInvocationRule
{
public:
Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
auto preparedCallee = prepareFunc( c, 0, callee, typeCheckedCallPat, tcc );
if( preparedCallee.isPoison() )
return PoisonValue();
auto callDecomp = Decompose( typeCheckedCallPat,
Val< pvec >()
);
const auto& typeCheckedRType = callDecomp->get()->terms().front();
auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
preparedCallee.setLocationId( loc );
auto ft = *FromValue< FuncType >( *EIRToValue( preparedCallee.type() ) );
auto argList = BuildArgListForCall( c, ft, typeCheckedArgs );
if( !argList )
return PoisonValue();
auto argsInstrSeq = BuildArgsInstrSeq( *argList );
auto argCount = VecSize( *argList );
auto result = BuildComputedValue( typeCheckedRType, argsInstrSeq, preparedCallee,
cir::Call( argCount, loc ) );
if( result.type() != GetValueType< void >() && !IsExternalFunc( callee ) )
{
if( cir::CanValueBeEagerlyEvaluated( result ) )
{
if( !verify::VerifyInstrSeq( c, *result.cir() ) )
return PoisonValue();
execute::VM vm;
result = execute::Evaluate( result, vm );
}
// Register the result for destruction.
if( auto cfg = GetCFG( c ) )
DeclareValue( c, result, cfg->getNewTemporaryIndex() );
}
return result;
}
Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
// TODO better description with the function's name if possible (we may need to explicitely store it in the func)
DiagnosticsContext dc( 0, true );
VerbosityContext vc( Verbosity::Normal, true );
return CompileFunc( c, callee );
}
};
void SetupFuncInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule(
ValueToEIR( ValuePattern( ANYTERM( _ ),
ValueToEIR( Value( TypeType(), VEC( TSID( func ),
ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ),
ANYTERM( _ ), ANYTERM( _ ) ) ) ),
ANYTERM( _ ) ) ),
make_shared< FuncInvocationRule >() );
}
}
|
Added bs/builtins/types/func/invocation/ghostfunc.cpp.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class GhostFuncInvocationRule : public BaseFuncInvocationRule
{
public:
Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
if( !CanInvokeGhostFuncs( c ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage( loc,
"ghost functions can't be called in this context." );
return PoisonValue();
}
auto callDecomp = Decompose( typeCheckedCallPat,
Val< pvec >()
);
const auto& typeCheckedRType = callDecomp->get()->terms().front();
auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
auto argCount = VecSize( typeCheckedArgs );
auto ft = *FromValue< FuncType >( *EIRToValue( callee.type() ) );
auto argList = BuildArgListForCall( c, ft, typeCheckedArgs );
if( !argList )
return PoisonValue();
auto argsInstrSeq = BuildArgsInstrSeq( *argList );
// A ghost call is a mutable reference to a ghost closure, using the special "GhostCall"
// as the instruction to compute its "address"
auto rt = ValueToEIR( ToValue( ReferenceType{ typeCheckedRType, MutAccessSpecifer() } ) );
return BuildComputedValue( rt, argsInstrSeq, callee, cir::GhostCall( argCount, loc ) );
}
Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
return callee;
}
};
void SetupGhostFuncInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule(
ValueToEIR( ValuePattern( ANYTERM( _ ),
ValueToEIR( Value( TypeType(), VEC( TSID( func ),
ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ),
ANYTERM( _ ),
TERM( static_cast< uint64_t >( FuncType::Kind::Ghost ) ) ) ) ),
ANYTERM( _ ) ) ),
make_shared< GhostFuncInvocationRule >() );
}
}
|
Added bs/builtins/types/func/invocation/intrinsic.cpp.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
#include "builtins/builtins.h"
#include "common.h"
using namespace goose::sema;
namespace goose::builtins
{
class IntrinsicFuncInvocationRule : public BaseFuncInvocationRule
{
public:
Value invoke( Context& c, LocationId loc, const Value& callee, const Term& args, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
auto preparedCallee = prepareFunc( c, 0, callee, typeCheckedCallPat, tcc );
if( preparedCallee.isPoison() )
return PoisonValue();
auto typeCheckedArgs = DropVectorTerm( typeCheckedCallPat, 1 );
preparedCallee.setLocationId( loc );
auto ft = *FromValue< FuncType >( *EIRToValue( preparedCallee.type() ) );
// Intrinsic call: we insert the context wrapper as first param,
// wrap all args with TypeWrapper< Value >, and execute the function directly.
auto argList = BuildArgListForIntrinsicCall( c, ft, typeCheckedArgs );
if( !argList )
return PoisonValue();
auto argsInstrSeq = BuildArgsInstrSeq( *argList );
auto argCount = VecSize( *argList );
execute::VM vm;
cir::InstrSeq is;
AppendToInstrSeq( is, argsInstrSeq, preparedCallee, cir::Call( argCount, loc ) );
if( !vm.execute( is ) )
return PoisonValue();
if( ft.returnType() == GetValueType< void >() )
return Value( GetValueType< void >(), 0U );
auto result = vm.pop();
if( !result )
return PoisonValue();
// Unwrap the returned value
auto unwrapped = FromValue< TypeWrapper< Value > >( *result );
return unwrapped ? *unwrapped : PoisonValue();
}
Value prepareFunc( const Context& c, LocationId funcValLocation, const Value& callee, const Term& typeCheckedCallPat, TypeCheckingContext& tcc ) const final
{
// TODO better description with the function's name if possible (we may need to explicitely store it in the func)
DiagnosticsContext dc( 0, true );
VerbosityContext vc( Verbosity::Normal, true );
return CompileFunc( c, callee );
}
};
void SetupIntrinsicFuncInvocationRule( Env& e )
{
e.invocationRuleSet()->addRule(
ValueToEIR( ValuePattern( ANYTERM( _ ),
ValueToEIR( Value( TypeType(), VEC( TSID( func ),
ANYTERM( _ ), ANYTERM( _ ), ANYTERM( _ ),
ANYTERM( _ ),
TERM( static_cast< uint64_t >( FuncType::Kind::Intrinsic ) ) ) ) ),
ANYTERM( _ ) ) ),
make_shared< IntrinsicFuncInvocationRule >() );
}
}
|
Added bs/builtins/types/func/invocation/invocation.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
#ifndef GOOSE_BUILTINS_TYPES_FUNC_INVOCATION_H
#define GOOSE_BUILTINS_TYPES_FUNC_INVOCATION_H
namespace goose::builtins
{
extern void SetupBuiltinEagerFuncInvocationRule( Env& e );
extern void SetupBuiltinFuncInvocationRule( Env& e );
extern void SetupBuiltinIntrinsicFuncInvocationRule( Env& e );
extern void SetupFuncInvocationRule( Env& e );
extern void SetupGhostFuncInvocationRule( Env& e );
extern void SetupIntrinsicFuncInvocationRule( Env& e );
extern const ptr< InvocationRule >& GetBuiltinFuncInvocationRule();
extern const ptr< InvocationRule >& GetBuiltinEagerFuncInvocationRule();
extern const ptr< InvocationRule >& GetBuiltinIntrinsicFuncInvocationRule();
template< typename F >
struct InvocationRuleSelector
{
static const auto& Get() { return GetBuiltinFuncInvocationRule(); }
};
template< typename R, typename... T >
struct InvocationRuleSelector< Eager< R >( T... ) >
{
static const auto& Get() { return GetBuiltinEagerFuncInvocationRule(); }
};
template< typename F >
struct InvocationRuleSelector< Intrinsic< F > >
{
static const auto& Get() { return GetBuiltinIntrinsicFuncInvocationRule(); }
};
template< typename F >
const ptr< InvocationRule >& GetInvocationRule()
{
return InvocationRuleSelector< F >::Get();
}
static inline void SetupFuncInvocationRules( Env& e )
{
SetupBuiltinEagerFuncInvocationRule( e );
SetupBuiltinFuncInvocationRule( e );
SetupBuiltinIntrinsicFuncInvocationRule( e );
SetupFuncInvocationRule( e );
SetupGhostFuncInvocationRule( e );
SetupIntrinsicFuncInvocationRule( e );
}
}
#endif
|
Deleted bs/builtins/types/func/invoke.cpp.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to bs/builtins/types/ghostcode/drop.cpp.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 |
{
// When a boolean is dropped into a ghost code block, it is appended as an assertion check.
RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< GhostCode > >, bool ) > >( e, e.extDropValue(),
[]( const Context& c, const Value& gcv, const Value& b )
{
auto gc = *FromValue< TypeWrapper< ptr< GhostCode > > >( gcv );
| | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
{
// When a boolean is dropped into a ghost code block, it is appended as an assertion check.
RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< GhostCode > >, bool ) > >( e, e.extDropValue(),
[]( const Context& c, const Value& gcv, const Value& b )
{
auto gc = *FromValue< TypeWrapper< ptr< GhostCode > > >( gcv );
gc->cfg()->currentBB()->append(
b, cir::Assert( b.locationId() )
);
} );
// When a ghost code block is dropped into a code builder, we append it using a GhostBranch terminator.
RegisterBuiltinFunc< Intrinsic< void ( TypeWrapper< ptr< CodeBuilder > >, TypeWrapper< ptr< GhostCode > > ) > >( e, e.extDropValue(),
[]( const Context& c, const Value& cbv, const Value& gcv )
{
|
| ︙ | ︙ |
Changes to bs/builtins/types/init.cpp.
| ︙ | ︙ | |||
25 26 27 28 29 30 31 |
// binary runtime code.
RegisterBuiltinFunc< Intrinsic< Value ( MutRefOfTypeT, ValueOfTypeT ) > >( e, e.extInitialize(),
[]( auto&& c, const Value& r, const Value& initVal )
{
G_VAL_ASSERT( r, !r.isConstant() );
auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
return BuildComputedValue( GetValueType< void >(),
| | | | | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
// binary runtime code.
RegisterBuiltinFunc< Intrinsic< Value ( MutRefOfTypeT, ValueOfTypeT ) > >( e, e.extInitialize(),
[]( auto&& c, const Value& r, const Value& initVal )
{
G_VAL_ASSERT( r, !r.isConstant() );
auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
return BuildComputedValue( GetValueType< void >(),
r, initVal, Store( refType.type(), initVal.locationId(), r.locationId() ) );
} );
// Default initialization for ct_int vars
RegisterBuiltinFunc< Intrinsic< Value ( CTIntMutRefType ) > >( e, e.extInitialize(),
[]( auto&& c, const Value& r )
{
G_VAL_ASSERT( r, !r.isConstant() );
auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
return BuildComputedValue( GetValueType< void >(),
r, ToValue( BigInt() ), Store( refType.type(), {}, r.locationId() ) );
} );
// Default initialization for ct_string vars
RegisterBuiltinFunc< Intrinsic< Value ( CTStringMutRefType ) > >( e, e.extInitialize(),
[]( auto&& c, const Value& r )
{
G_VAL_ASSERT( r, !r.isConstant() );
auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
return BuildComputedValue( GetValueType< void >(),
r, ToValue( ""s ), Store( refType.type(), {}, r.locationId() ) );
} );
}
}
|
Changes to bs/builtins/types/localvar/invoke.cpp.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
{
public:
bool canBeInvoked( const Context& c, const Value& callee ) const final
{
auto lv = *FromValue< LocalVar >( callee );
auto val = BuildComputedValue( lv.type(),
| | > | > | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
{
public:
bool canBeInvoked( const Context& c, const Value& callee ) const final
{
auto lv = *FromValue< LocalVar >( callee );
auto val = BuildComputedValue( lv.type(),
GetAddrFromLocalVar( lv ),
cir::Load( lv.type(), {} ) );
return sema::CanBeInvoked( c, val );
}
Value resolveInvocation( Context& c, LocationId locationId, const Value& callee, const Term& args ) const final
{
auto lv = *FromValue< LocalVar >( callee );
auto val = BuildComputedValue( lv.type(),
GetAddrFromLocalVar( lv ),
cir::Load( lv.type(), locationId ) );
return sema::GetInvocationRule( *c.env(), val )->resolveInvocation( c, locationId, val, args );
}
};
void SetupLocalVarInvocationRule( Env& e )
{
|
| ︙ | ︙ |
Changes to bs/builtins/types/localvar/localvar.cpp.
| ︙ | ︙ | |||
35 36 37 38 39 40 41 |
if( !typeVal.isType() )
typeVal = ToType( c, typeVal );
if( !ParseTypePredicates( c, typeVal ) )
return PoisonValue();
LocalVar lv( name, ValueToEIR( typeVal ), index );
| | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
if( !typeVal.isType() )
typeVal = ToType( c, typeVal );
if( !ParseTypePredicates( c, typeVal ) )
return PoisonValue();
LocalVar lv( name, ValueToEIR( typeVal ), index );
bb->append( AllocVar( typeVal, index, locId ) );
Value initResult;
{
DiagnosticsContext dc( 0, "When invoking Initialize." );
if( initializer )
|
| ︙ | ︙ | |||
180 181 182 183 184 185 186 |
// Retrieve the texpr's location and set it on the inferred type. This way if an
// error occurs later with it, for instance when calling LowerTypeForRuntime on it during codegen,
// it will have a meaningful location for the error message to attach itself on.
auto typeLoc = EIRToValue( typeTExpr )->locationId();
LocalVar lv( name, type, index );
| | | 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
// Retrieve the texpr's location and set it on the inferred type. This way if an
// error occurs later with it, for instance when calling LowerTypeForRuntime on it during codegen,
// it will have a meaningful location for the error message to attach itself on.
auto typeLoc = EIRToValue( typeTExpr )->locationId();
LocalVar lv( name, type, index );
bb->append( AllocVar( EIRToValue( lv.type() )->setLocationId( typeLoc ), index, locId ) );
DiagnosticsContext dc( 0, "When invoking Initialize." );
auto initResult = InvokeOverloadSet( c,
c.env()->extInitialize(),
MakeTuple( ToValue( lv ).setLocationId( locId ), initVal ) );
|
| ︙ | ︙ |
Changes to bs/builtins/types/lower.cpp.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 |
} );
// Default implementation of LowerConstantForVerification():
// Do nothing.
RegisterBuiltinFunc< Intrinsic< Value ( Value ) > >( e, e.extLowerConstantForVerification(),
[]( const Context& c, const Value& v )
{
return v;
} );
}
}
| > > > > > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
} );
// Default implementation of LowerConstantForVerification():
// Do nothing.
RegisterBuiltinFunc< Intrinsic< Value ( Value ) > >( e, e.extLowerConstantForVerification(),
[]( const Context& c, const Value& v )
{
return v;
} );
// Default implementation of LowerConstantForRuntime():
// Do nothing.
RegisterBuiltinFunc< Intrinsic< Value ( Value ) > >( e, e.extLowerConstantForRuntime(),
[]( const Context& c, const Value& v )
{
return v;
} );
}
}
|
Changes to bs/builtins/types/pretty.cpp.
| ︙ | ︙ | |||
40 41 42 43 44 45 46 |
{
auto v = *EIRToValue( t );
auto ty = EIRToValue( v.type() );
out << "value[ ";
pp.print( out, ty ? ty->val() : v.type() );
out << " ]";
if( v.cir() )
| > | > > > > | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
{
auto v = *EIRToValue( t );
auto ty = EIRToValue( v.type() );
out << "value[ ";
pp.print( out, ty ? ty->val() : v.type() );
out << " ]";
if( v.cir() )
{
out << "{ ";
for( const auto& instr : *v.cir() )
out << instr << ' ';
out << "}";
}
return true;
} );
pp.addRule( ValueToEIR( ValuePattern( TSID( constant ),
ValueToEIR( Value( TypeType(), VEC( TSID( decl ), ANYTERM( _ ) ) ) ),
ANYTERM( _ ) ) ),
[&]( auto&& out, auto&& t )
|
| ︙ | ︙ |
Changes to bs/builtins/types/propositions/propositions.cpp.
1 2 3 4 5 6 7 8 |
#include "builtins/builtins.h"
namespace goose::builtins
{
size_t Propositions::hash() const
{
if( !m_hash )
{
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include "builtins/builtins.h"
namespace goose::builtins
{
size_t Propositions::hash() const
{
if( !m_hash )
{
auto g1 = ContainerHashGenerator( m_propositions );
m_hash = llvm::hash_combine_range( g1.begin(), g1.end() );
}
return *m_hash;
}
bool Propositions::parse( const Context& c )
{
|
| ︙ | ︙ |
Changes to bs/builtins/types/reference/init.cpp.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
ReferenceType::PatternMutableOf< ReferenceType::PatternXOfTypeT > >,
CustomPattern< Value, ReferenceType::PatternXOfTypeT > ) > >( e, e.extInitialize(),
[]( auto&& c, const Value& r, const Value& initVal )
{
G_VAL_ASSERT( r, !r.isConstant() );
auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
return BuildComputedValue( GetValueType< void >(),
| | | 12 13 14 15 16 17 18 19 20 21 22 |
ReferenceType::PatternMutableOf< ReferenceType::PatternXOfTypeT > >,
CustomPattern< Value, ReferenceType::PatternXOfTypeT > ) > >( e, e.extInitialize(),
[]( auto&& c, const Value& r, const Value& initVal )
{
G_VAL_ASSERT( r, !r.isConstant() );
auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
return BuildComputedValue( GetValueType< void >(),
r, initVal, Store( refType.type(), initVal.locationId(), r.locationId() ) );
} );
}
}
|
Changes to bs/builtins/types/reference/reference.cpp.
| ︙ | ︙ | |||
48 49 50 51 52 53 54 |
const Term& ReferenceType::PatternXOfTypeT::GetPattern()
{
static auto pattern = ValueToEIR( ToValue( ReferenceType( HOLE( "T"_sid, TSID( ttvar ) ), HOLE( "X"_sid ) ) ) );
return pattern;
}
// Returns an instruction that computes the address of whatever's contained in the locvar.
| | > | > | | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
const Term& ReferenceType::PatternXOfTypeT::GetPattern()
{
static auto pattern = ValueToEIR( ToValue( ReferenceType( HOLE( "T"_sid, TSID( ttvar ) ), HOLE( "X"_sid ) ) ) );
return pattern;
}
// Returns an instruction that computes the address of whatever's contained in the locvar.
cir::Instruction GetAddrFromLocalVar( const LocalVar& lv )
{
// TODO LOC: may need loc in LocalVar
return cir::VarAddr( lv.index(), lv.type(), {} );
}
Value BuildLocalVarMutRef( const LocalVar& lv )
{
// TODO LOC: may need loc in LocalVar
auto rt = ValueToEIR( ToValue( ReferenceType{ lv.type(), MutAccessSpecifer() } ) );
return BuildComputedValue( move( rt ), cir::VarAddr( lv.index(), lv.type(), {} ) );
}
const Term& MutAccessSpecifer()
{
static auto al = ValueToEIR( ToValue( AccessSpecifier( "mut"_sid ) ) );
return al;
}
|
| ︙ | ︙ |
Changes to bs/builtins/types/reference/reference.h.
| ︙ | ︙ | |||
132 133 134 135 136 137 138 |
// Not an enum because we want to be able
// to have holes there and it's a pain in
// the ass
Term m_behavior;
};
| | | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
// Not an enum because we want to be able
// to have holes there and it's a pain in
// the ass
Term m_behavior;
};
extern cir::Instruction GetAddrFromLocalVar( const LocalVar& lv );
extern Value BuildLocalVarMutRef( const LocalVar& lv );
}
namespace goose::eir
{
template<>
struct Bridge< builtins::ReferenceType >
|
| ︙ | ︙ |
Changes to bs/builtins/types/reference/typecheck.cpp.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 24 |
if( !refval )
co_return;
auto refType = FromValue< ReferenceType >( *EIRToValue( refval->type() ) );
if( !refType )
co_return;
auto content = ValueToEIR( BuildComputedValue( refType->type(),
| > | | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
if( !refval )
co_return;
auto refType = FromValue< ReferenceType >( *EIRToValue( refval->type() ) );
if( !refType )
co_return;
auto loc = refval->locationId() ;
auto content = ValueToEIR( BuildComputedValue( refType->type(),
*refval, cir::Load( refType->type(), loc ) )
.setLocationId( loc ) );
// TypeCheck the param with the ref's content
co_yield TypeCheck( lhs, content, tcc );
}
TCGen TypeCheckingBuildTempRef( const Term& lhs, const Term& rhs, const TypeCheckingContext& tcc )
{
|
| ︙ | ︙ | |||
59 60 61 62 63 64 65 66 |
auto rhsVal = *EIRToValue( rhs );
auto cfg = GetCFG( tcc.context() );
if( !cfg )
return nullopt;
// TODO create an ext point for this
auto tempIndex = cfg->getNewTemporaryIndex();
| > | | | 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
auto rhsVal = *EIRToValue( rhs );
auto cfg = GetCFG( tcc.context() );
if( !cfg )
return nullopt;
// TODO create an ext point for this
auto loc = rhsVal.locationId();
auto tempIndex = cfg->getNewTemporaryIndex();
return ValueToEIR( BuildComputedValue( ValueToEIR( ToValue( rt ) ), rhsVal,
TempAddr( tempIndex, loc ) ).setLocationId( loc ) );
} );
// Override the weight because we don't want
// this solution to count more than directly using
// the value without wrapping it into a tempref
SetWeight( wrapped, GetWeight( rhs ) - 1 );
co_yield { move( wrapped ), tcc };
|
| ︙ | ︙ | |||
257 258 259 260 261 262 263 264 |
auto ltype = ValuePatternFromEIR( lhs )->type();
auto rrefVal = *EIRToValue( rhs );
auto rrType = FromValue< ReferenceType >( *EIRToValue( rrefVal.type() ) );
if( !rrType )
co_return;
auto content = ValueToEIR( BuildComputedValue( rrType->type(),
| > | | 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
auto ltype = ValuePatternFromEIR( lhs )->type();
auto rrefVal = *EIRToValue( rhs );
auto rrType = FromValue< ReferenceType >( *EIRToValue( rrefVal.type() ) );
if( !rrType )
co_return;
auto loc = rrefVal.locationId();
auto content = ValueToEIR( BuildComputedValue( rrType->type(),
rrefVal, cir::Load( rrType->type(), loc ) ).setLocationId( loc ) );
co_yield TypeCheck( lhs, content, tcc );
} );
// Implicit referencing of non-variables against a non mutable ref: build a tempref
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
|
| ︙ | ︙ |
Changes to bs/builtins/types/runtime/init.cpp.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 |
// Initialization for integer vars
RegisterBuiltinFunc< Intrinsic< Value ( IntegerMutRefType, IntegerType ) > >( e, e.extInitialize(),
[]( auto&& c, const Value& r, const Value& initVal )
{
G_VAL_ASSERT( r, !r.isConstant() );
auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
return BuildComputedValue( GetValueType< void >(),
| | | 16 17 18 19 20 21 22 23 24 25 26 |
// Initialization for integer vars
RegisterBuiltinFunc< Intrinsic< Value ( IntegerMutRefType, IntegerType ) > >( e, e.extInitialize(),
[]( auto&& c, const Value& r, const Value& initVal )
{
G_VAL_ASSERT( r, !r.isConstant() );
auto refType = *FromValue< ReferenceType >( *EIRToValue( r.type() ) );
return BuildComputedValue( GetValueType< void >(),
r, initVal, Store( refType.type(), initVal.locationId(), r.locationId() ) );
} );
}
}
|
Changes to bs/builtins/types/template/invoke.cpp.
| ︙ | ︙ | |||
45 46 47 48 49 50 51 |
{
DiagnosticsContext dc( loc, loc.invalid() ? "" : "Called here.", false );
auto instanceFunc = InstantiateTFunc( c, callee, checkedCallPat, tcc );
if( instanceFunc.isPoison() )
return PoisonValue();
| > > > > | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
{
DiagnosticsContext dc( loc, loc.invalid() ? "" : "Called here.", false );
auto instanceFunc = InstantiateTFunc( c, callee, checkedCallPat, tcc );
if( instanceFunc.isPoison() )
return PoisonValue();
auto pIR = GetInvocationRule( *c.env(), instanceFunc );
G_VAL_ASSERT( callee, pIR );
G_VAL_ASSERT( callee, pIR->canBeInvoked( c, instanceFunc ) );
return pIR->resolveInvocation( c, loc, instanceFunc, args );
}
optional< Term > getSignature( const Value& callee ) const final
{
auto tfuncVal = FromValue< TFunc >( callee );
assert( tfuncVal );
return tfuncVal->signature();
|
| ︙ | ︙ |
Changes to bs/builtins/types/tuple/init.cpp.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 27 28 29 30 |
DiagnosticsManager::GetInstance().emitErrorMessage( 0, "Incompatible tuple sizes." );
return;
}
ForEachInTupleType( tupType, [&]( auto&& t )
{
auto elemType = *EIRToValue( t );
// Create a mutable reference to the element to initialize
ReferenceType rt( t, MutAccessSpecifer() );
auto elemRef = BuildComputedValue( ValueToEIR( ToValue( rt ) ),
| > | | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
DiagnosticsManager::GetInstance().emitErrorMessage( 0, "Incompatible tuple sizes." );
return;
}
ForEachInTupleType( tupType, [&]( auto&& t )
{
auto elemType = *EIRToValue( t );
auto loc = elemType.locationId();
// Create a mutable reference to the element to initialize
ReferenceType rt( t, MutAccessSpecifer() );
auto elemRef = BuildComputedValue( ValueToEIR( ToValue( rt ) ),
tupRef, Select( index, loc ) ).setLocationId( loc );
auto elemInit = *EIRToValue( GetTupleElement( initTup, index++ ) );
DiagnosticsContext dc( elemType.locationId(), "When invoking Initialize." );
auto init = InvokeOverloadSet( c, c.env()->extInitialize(),
MakeTuple( elemRef, move( elemInit ) ) );
|
| ︙ | ︙ | |||
57 58 59 60 61 62 63 64 65 66 67 |
uint32_t index = 0;
auto tupType = *EIRToValue( refType.type() );
ForEachInTupleType( tupType, [&]( auto&& t )
{
auto elemType = *EIRToValue( t );
// Create a mutable reference to the element to initialize
ReferenceType rt( t, MutAccessSpecifer() );
auto elemRef = BuildComputedValue( ValueToEIR( ToValue( rt ) ),
| > | | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
uint32_t index = 0;
auto tupType = *EIRToValue( refType.type() );
ForEachInTupleType( tupType, [&]( auto&& t )
{
auto elemType = *EIRToValue( t );
auto loc = elemType.locationId();
// Create a mutable reference to the element to initialize
ReferenceType rt( t, MutAccessSpecifer() );
auto elemRef = BuildComputedValue( ValueToEIR( ToValue( rt ) ),
tupRef, Select( index++, loc ) ).setLocationId( loc );
DiagnosticsContext dc( elemType.locationId(), "When invoking Initialize." );
auto init = InvokeOverloadSet( c, c.env()->extInitialize(),
MakeTuple( elemRef ) );
DiagnosticsContext dc2( elemType.locationId(), "When invoking DropValue." );
InvokeOverloadSet( c, c.env()->extDropValue(),
|
| ︙ | ︙ |
Changes to bs/builtins/types/tuple/typecheck.cpp.
| ︙ | ︙ | |||
31 32 33 34 35 36 37 |
{
auto param = ParamPat( GetTupleTypeElement( tupType, index ) );
auto argType = GetTupleElementType( tupArg, index );
ReferenceType rt( argType, ConstAccessSpecifer() );
G_VAL_ASSERT( tupArgRef, !tupArgRef.isConstant() );
| < | | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
{
auto param = ParamPat( GetTupleTypeElement( tupType, index ) );
auto argType = GetTupleElementType( tupArg, index );
ReferenceType rt( argType, ConstAccessSpecifer() );
G_VAL_ASSERT( tupArgRef, !tupArgRef.isConstant() );
auto argRef = ValueToEIR( BuildComputedValue( ValueToEIR( ToValue( rt ) ),
tupArgRef, cir::Select( index, tupArg.locationId() ) ) );
auto tupSize = TupleTypeSize( tupType );
for( auto&& [s,tcc] : TypeCheck( param, argRef, tcc ) )
{
auto val = ValuePatternFromEIR( s );
assert( val );
|
| ︙ | ︙ | |||
110 111 112 113 114 115 116 |
auto tupType = *EIRToValue( ltup->type() );
assert( TupleTypeSize( tupType ) == TupleTypeSize( *EIRToValue( rtup->type() ) ) );
// TODO create an ext point for this instead of going directly thru cfg
auto tempIndex = cfg->getNewTemporaryIndex();
auto rtupref = BuildComputedValue( ValueToEIR( ToValue( ReferenceType{ rtup->type(), ConstAccessSpecifer() } ) ),
| | | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
auto tupType = *EIRToValue( ltup->type() );
assert( TupleTypeSize( tupType ) == TupleTypeSize( *EIRToValue( rtup->type() ) ) );
// TODO create an ext point for this instead of going directly thru cfg
auto tempIndex = cfg->getNewTemporaryIndex();
auto rtupref = BuildComputedValue( ValueToEIR( ToValue( ReferenceType{ rtup->type(), ConstAccessSpecifer() } ) ),
*rtup, cir::TempAddr( tempIndex, rtup->locationId() ) );
co_yield TypeCheckComputedTuple( tcc, tupType, *rtup, rtupref, 0, EmptyTuple() );
} );
// Single element tuple unwrapping rules: if we encounter such a tuple, attempt to typecheck
// its contained value with whatever's on the other side.
e.typeCheckingRuleSet()->addTypeCheckingRule( TCRINFOS,
|
| ︙ | ︙ |
Changes to bs/builtins/types/types.cpp.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
SetupPredicatesTypeChecking( e );
SetupBasicTypes( e );
SetupBasicTypesPrettyPrinting();
SetupTupleTypeChecking( e );
| | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
SetupPredicatesTypeChecking( e );
SetupBasicTypes( e );
SetupBasicTypesPrettyPrinting();
SetupTupleTypeChecking( e );
SetupFuncInvocationRules( e );
SetupFunctionTypeChecking( e );
SetupFunctionLowering( e );
SetupTemplateRules( e );
SetupTemplateFunctionInvocationRule( e );
SetupTemplateFunctionTypeChecking( e );
SetupTDeclTypeChecking( e );
|
| ︙ | ︙ |
Changes to bs/builtins/types/types.h.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include "ghostcode/ghostcode.h" #include "func/bfunc.h" #include "func/bintrinsic.h" #include "func/functype.h" #include "func/func.h" #include "func/build.h" #include "basic.h" #include "decl.h" #include "runtime/runtime.h" #include "wrapper.h" #include "template/tvar.h" | > | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #include "ghostcode/ghostcode.h" #include "func/bfunc.h" #include "func/bintrinsic.h" #include "func/functype.h" #include "func/func.h" #include "func/build.h" #include "func/invocation/invocation.h" #include "basic.h" #include "decl.h" #include "runtime/runtime.h" #include "wrapper.h" #include "template/tvar.h" |
| ︙ | ︙ |
Changes to bs/cir/allocvar.h.
1 2 3 4 5 |
#ifndef GOOSE_CIR_ALLOCVAR_H
#define GOOSE_CIR_ALLOCVAR_H
namespace goose::cir
{
| | | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#ifndef GOOSE_CIR_ALLOCVAR_H
#define GOOSE_CIR_ALLOCVAR_H
namespace goose::cir
{
class AllocVar : public BaseInstr< 0, false >
{
public:
template< typename T >
AllocVar( T&& type, uint32_t index, LocationId loc ) :
BaseInstr( loc ),
m_type( forward< T >( type ) ),
m_index( index )
{}
const auto& type() const { return m_type; }
const auto& index() const { return m_index; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return true; }
bool operator<( const AllocVar& rhs ) const
{
if( m_index != rhs.m_index )
return m_index < rhs.m_index;
return m_type < rhs.m_type;
}
|
| ︙ | ︙ |
Changes to bs/cir/arith.h.
1 2 3 4 5 6 7 8 |
#ifndef GOOSE_CIR_ARITH_H
#define GOOSE_CIR_ARITH_H
namespace goose::cir
{
class Add : public BinaryOp
{
public:
| | | > | | | > | | | > | | | > | | | > | | | > | | | > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
#ifndef GOOSE_CIR_ARITH_H
#define GOOSE_CIR_ARITH_H
namespace goose::cir
{
class Add : public BinaryOp
{
public:
Add( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const Add& ins )
{
return out << "ADD";
}
};
class Sub : public BinaryOp
{
public:
Sub( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const Sub& ins )
{
return out << "SUB";
}
};
class Mul : public BinaryOp
{
public:
Mul( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const Mul& ins )
{
return out << "MUL";
}
};
class UDiv : public BinaryOp
{
public:
UDiv( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const UDiv& ins )
{
return out << "UDIV";
}
};
class SDiv : public BinaryOp
{
public:
SDiv( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const SDiv& ins )
{
return out << "SDIV";
}
};
class URem : public BinaryOp
{
public:
URem( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const URem& ins )
{
return out << "UREM";
}
};
class SRem : public BinaryOp
{
public:
SRem( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const SRem& ins )
{
return out << "SREM";
}
};
}
#endif
|
Changes to bs/cir/ass.h.
1 2 3 4 5 6 7 8 9 |
#ifndef GOOSE_CIR_ASS_H
#define GOOSE_CIR_ASS_H
// This file is not called "assert.h" because that fucks the cassert header up
// when it tries to include assert.h.
// "ass" is both a good shorthand and an accurate description of the problem
namespace goose::cir
{
| | < | < > < < > | | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#ifndef GOOSE_CIR_ASS_H
#define GOOSE_CIR_ASS_H
// This file is not called "assert.h" because that fucks the cassert header up
// when it tries to include assert.h.
// "ass" is both a good shorthand and an accurate description of the problem
namespace goose::cir
{
class Assert : public BaseInstr< 1, false >
{
public:
Assert( LocationId loc ) :
BaseInstr( loc )
{}
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return true; }
bool operator<( const Assert& rhs ) const
{
return false;
}
friend ostream& operator<<( ostream& out, const Assert& ins )
{
return out << "ASSERT";
}
};
}
#endif
|
Changes to bs/cir/basicblock.h.
| ︙ | ︙ | |||
42 43 44 45 46 47 48 |
const auto& loopEdges() const { return m_loopEdges; }
uint32_t loopId() const { return m_loopId; }
bool isLoopHeader() const { return m_isLoopHeader; }
void setLoopId( uint32_t id ) { m_loopId = id; }
void setLoopHeader() { m_isLoopHeader = true; }
| < < < | > | > > > > > > | | | | | | | | < > | | | > | < > | 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
const auto& loopEdges() const { return m_loopEdges; }
uint32_t loopId() const { return m_loopId; }
bool isLoopHeader() const { return m_isLoopHeader; }
void setLoopId( uint32_t id ) { m_loopId = id; }
void setLoopHeader() { m_isLoopHeader = true; }
auto* llvmBB() { return m_llvmBB; }
void setLLVMBB( llvm::BasicBlock* pBB ) { m_llvmBB = pBB; }
auto empty() const { return m_instructions.empty(); }
const auto& instructions() const { return m_instructions; }
using RunnableInstructions = llvm::SmallVector< Instruction, 16 >;
const RunnableInstructions* runnableInstructions() const
{
if( m_dirty )
{
m_dirty = false;
m_runnableInstructions.clear();
if( !FilterVerificationInstructions( m_instructions, m_runnableInstructions ) )
return nullptr;
}
return &m_runnableInstructions;
}
void clear()
{
m_instructions.clear();
m_dirty = true;
}
template< typename... I >
void append( I&&... instrs )
{
AppendToInstrSeq( m_instructions, forward< I >( instrs )... );
m_dirty = true;
}
template< typename T >
void setTerminator( T&& terminator );
const auto& terminator() const { return m_terminator; }
bool canBeExecuted() const
|
| ︙ | ︙ | |||
96 97 98 99 100 101 102 |
bool codeGenStarted() const { return m_codeGenStarted; }
void setCodeGenStarted( bool b = true ) { m_codeGenStarted = b; }
template< typename F >
void forEachSuccessor( F&& func ) const;
private:
| | > > > | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
bool codeGenStarted() const { return m_codeGenStarted; }
void setCodeGenStarted( bool b = true ) { m_codeGenStarted = b; }
template< typename F >
void forEachSuccessor( F&& func ) const;
private:
InstrSeq m_instructions;
mutable RunnableInstructions m_runnableInstructions;
optional< Terminator > m_terminator;
weak_ptr< CFG > m_owner;
llvm::SmallVector< uint32_t, 8 > m_backEdges;
llvm::SmallVector< uint32_t, 8 > m_loopEdges;
llvm::BasicBlock* m_llvmBB = nullptr;
uint32_t m_index = 0;
uint32_t m_loopId = 0; // Id of the header of the loop that this block belongs to, or 0.
eir::LocationId m_locId = 0; // An optional location id. Used by the verifier on loop headers
// to indicate the location of a loop that it failed to verify.
bool m_isLoopHeader = false;
bool m_canBeExecuted = true;
bool m_canBeEagerlyEvaluated = true;
mutable bool m_dirty = false;
mutable bool m_codeGenStarted = false;
};
}
#endif
|
Changes to bs/cir/basicblock.inl.
1 2 3 4 5 |
#ifndef GOOSE_CIR_BASICBLOCK_INL
#define GOOSE_CIR_BASICBLOCK_INL
namespace goose::cir
{
| < < < < < < < < < < < < < < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 |
#ifndef GOOSE_CIR_BASICBLOCK_INL
#define GOOSE_CIR_BASICBLOCK_INL
namespace goose::cir
{
template< typename T >
void BasicBlock::setTerminator( T&& terminator )
{
m_terminator = forward< T >( terminator );
m_terminator->addCFGEdges( m_owner.lock(), index() );
}
|
| ︙ | ︙ |
Deleted bs/cir/binaryop.cpp.
|
| < < < < < < < < < < < < < < < < |
Changes to bs/cir/binaryop.h.
1 2 3 4 5 |
#ifndef GOOSE_CIR_BINARYINSTR_H
#define GOOSE_CIR_BINARYINSTR_H
namespace goose::cir
{
| | < | | < < < < | | > < | < < < < < < < < < < < < < < < < < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#ifndef GOOSE_CIR_BINARYINSTR_H
#define GOOSE_CIR_BINARYINSTR_H
namespace goose::cir
{
class BinaryOp : public BaseInstr< 2, true >
{
public:
BinaryOp( LocationId loc ) :
BaseInstr( loc )
{}
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
bool haveSideEffects() const { return false; }
bool operator<( const BinaryOp& rhs ) const
{
return false;
}
};
}
#endif
|
Changes to bs/cir/bitwise.h.
1 2 3 4 5 6 7 8 |
#ifndef GOOSE_CIR_BITWISE_H
#define GOOSE_CIR_BITWISE_H
namespace goose::cir
{
class Shl : public BinaryOp
{
public:
| | | > | | | > | | | > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#ifndef GOOSE_CIR_BITWISE_H
#define GOOSE_CIR_BITWISE_H
namespace goose::cir
{
class Shl : public BinaryOp
{
public:
Shl( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const Shl& ins )
{
return out << "SHL";
}
};
class LShr : public BinaryOp
{
public:
LShr( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const LShr& ins )
{
return out << "LSHR";
}
};
class AShr : public BinaryOp
{
public:
AShr( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const AShr& ins )
{
return out << "ASHR";
}
};
}
#endif
|
Changes to bs/cir/branch.h.
| ︙ | ︙ | |||
23 24 25 26 27 28 29 |
private:
wptr< BasicBlock > m_dest;
};
class CondBranch
{
public:
| | | < < | < < < < | < < < < | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
private:
wptr< BasicBlock > m_dest;
};
class CondBranch
{
public:
template< typename BT, typename BF >
CondBranch( BT&& trueDest, BF&& falseDest ) :
m_trueDest( forward< BT >( trueDest ) ),
m_falseDest( forward< BF >( falseDest ) )
{}
const auto& trueDest() const { return m_trueDest; }
const auto& falseDest() const { return m_falseDest; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex );
private:
wptr< BasicBlock > m_trueDest;
wptr< BasicBlock > m_falseDest;
};
// A special terminator that points to a basic block to use only
// during verification (containing "ghost code") and to a continuation
// basic block
|
| ︙ | ︙ |
Deleted bs/cir/call.cpp.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to bs/cir/call.h.
1 2 3 4 5 |
#ifndef GOOSE_CIR_CALL_H
#define GOOSE_CIR_CALL_H
namespace goose::cir
{
| | < | | | < | | | > < < | | | > > > | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#ifndef GOOSE_CIR_CALL_H
#define GOOSE_CIR_CALL_H
namespace goose::cir
{
class Call : public BaseInstr< 1, true > // Can pop more depending on arg count
{
public:
Call( uint32_t numArgs, LocationId loc ) :
BaseInstr( loc ),
m_numArgs( numArgs )
{}
const auto& numArgs() const { return m_numArgs; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
bool haveSideEffects() const { return true; }
bool operator<( const Call& rhs ) const
{
return m_numArgs < rhs.m_numArgs;
}
friend ostream& operator<<( ostream& out, const Call& ins )
{
return out << "CALL";
}
private:
uint32_t m_numArgs = 0;
};
}
#endif
|
Changes to bs/cir/cfg.h.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
{
public:
CFG( uint32_t numParams ) :
m_temporariesCount( numParams )
{}
bool isPoisoned() const { return m_poisoned; }
| | > > > | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
{
public:
CFG( uint32_t numParams ) :
m_temporariesCount( numParams )
{}
bool isPoisoned() const { return m_poisoned; }
void poison()
{
m_poisoned = true;
}
const auto& entryBB() const { return m_basicBlocks.front(); }
const auto& lastBB() const { return m_basicBlocks.back(); }
const auto& currentBB() const { return m_currentBB; }
template< typename T >
void setCurrentBB( T&& pBB )
|
| ︙ | ︙ | |||
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
const auto& getBB( uint32_t index ) const { return m_basicBlocks[index - 1]; }
auto count() const { return m_basicBlocks.size(); }
const ptr< BasicBlock >& createBB();
auto getNewTemporaryIndex() { return m_temporariesCount++; }
// Clear the llvm basic block pointers from the entire cfg.
void unbindFromLLVM();
bool canBeExecuted() const;
bool canBeEagerlyEvaluated() const;
template< typename F >
void forEachBB( F&& func )
{
for( auto&& bb : m_basicBlocks )
func( bb );
}
| > | | | | | | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
const auto& getBB( uint32_t index ) const { return m_basicBlocks[index - 1]; }
auto count() const { return m_basicBlocks.size(); }
const ptr< BasicBlock >& createBB();
auto getNewTemporaryIndex() { return m_temporariesCount++; }
auto temporariesCount() const { return m_temporariesCount; }
// Clear the llvm basic block pointers from the entire cfg.
void unbindFromLLVM();
bool canBeExecuted() const;
bool canBeEagerlyEvaluated() const;
template< typename F >
void forEachBB( F&& func )
{
for( auto&& bb : m_basicBlocks )
func( bb );
}
void setStorageLocationModifiedByLoop( uint32_t loopId, const eir::Term& type, const StorageLocation& sloc )
{
m_loopModifiedStorageLocations.emplace( make_pair( loopId, sloc ), type );
}
template< typename F >
void forEachStorageLocationModifiedByLoop( uint32_t loopId, F&& func ) const
{
auto begin = m_loopModifiedStorageLocations.lower_bound( { loopId, Address{ Address::Origin::Stack, 0, 0 } } );
auto end = m_loopModifiedStorageLocations.upper_bound( { loopId, monostate() } );
for( auto it = begin; it != end; ++it )
func( it->second, it->first.second );
}
template< typename F >
void forEachReachableBlock( const ptr< BasicBlock >& bb, F&& func ) const
|
| ︙ | ︙ | |||
140 141 142 143 144 145 146 |
// All the edges of the CFG in SrcBBIndex, DestBBIndex form
unordered_multimap< uint32_t, uint32_t > m_edges;
// For each BB, store the index of its immediate dominator.
// May be be empty if it has not (yet) been computed.
vector< uint32_t > m_idoms;
| | | | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
// All the edges of the CFG in SrcBBIndex, DestBBIndex form
unordered_multimap< uint32_t, uint32_t > m_edges;
// For each BB, store the index of its immediate dominator.
// May be be empty if it has not (yet) been computed.
vector< uint32_t > m_idoms;
// For each loop, store all of the storage locations modified
// during that loop.
// May be be empty if it has not (yet) been computed.
multimap< pair< uint32_t, StorageLocation >, eir::Term > m_loopModifiedStorageLocations;
// The number of temporary indices used by this CFG.
uint32_t m_temporariesCount = 0;
uint32_t m_loopCount = 0;
bool m_poisoned = false;
|
| ︙ | ︙ |
Changes to bs/cir/cfgviz.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include "cir/cir.h"
#include "builtins/builtins.h"
using namespace goose::builtins;
namespace goose::cir
{
void CfgViz( GraphVizBuilder& builder, const CFG& cfg )
{
CfgViz( builder, cfg.entryBB() );
}
void CfgViz( GraphVizBuilder& builder, const ptr< BasicBlock >& bb )
{
| > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include "cir/cir.h"
#include "builtins/builtins.h"
using namespace goose::builtins;
namespace goose::cir
{
void CfgViz( GraphVizBuilder& builder, const Func& func )
{
if( func.body() )
CfgViz( builder, *func.body() );
}
void CfgViz( GraphVizBuilder& builder, const CFG& cfg )
{
CfgViz( builder, cfg.entryBB() );
}
void CfgViz( GraphVizBuilder& builder, const ptr< BasicBlock >& bb )
{
|
| ︙ | ︙ | |||
22 23 24 25 26 27 28 |
sstr << ", L" << bb->loopId();
if( bb->isLoopHeader() )
sstr << ", LH";
GraphVizBuilder::Node n( builder, bb.get(), sstr.str().c_str() );
| | | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
sstr << ", L" << bb->loopId();
if( bb->isLoopHeader() )
sstr << ", LH";
GraphVizBuilder::Node n( builder, bb.get(), sstr.str().c_str() );
for( auto&& instr : bb->instructions() )
CfgViz( builder, instr );
if( bb->terminator() )
CfgViz( builder, *bb->terminator() );
}
void CfgViz( GraphVizBuilder& builder, const eir::Value& val )
|
| ︙ | ︙ | |||
44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
GraphVizBuilder::Color col( builder );
builder.output() << val;
return;
}
CfgViz( builder, *val.cir() );
}
void CfgViz( GraphVizBuilder& builder, const Instruction& instr )
{
visit( [&]( auto&& ins )
{
CfgViz( builder, ins );
}, instr.content() );
| > > > > > > | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
GraphVizBuilder::Color col( builder );
builder.output() << val;
return;
}
CfgViz( builder, *val.cir() );
}
void CfgViz( GraphVizBuilder& builder, const InstrSeq& is )
{
for( const auto& instr : is )
CfgViz( builder, instr );
}
void CfgViz( GraphVizBuilder& builder, const Instruction& instr )
{
visit( [&]( auto&& ins )
{
CfgViz( builder, ins );
}, instr.content() );
|
| ︙ | ︙ | |||
72 73 74 75 76 77 78 |
void CfgViz( GraphVizBuilder& builder, const Call& instr )
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
| > > | > > > > > > > > > > > > > | > > > > > > > > > > | | | < < < < < < | | < | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
void CfgViz( GraphVizBuilder& builder, const Call& instr )
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
builder.output() << "Call " << instr.numArgs();
}
void CfgViz( GraphVizBuilder& builder, const Constant& instr )
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
stringstream sstr;
sstr << instr.value();
for( auto c : sstr.str() )
{
switch( c )
{
case '<':
builder.output() << "<";
break;
case '>':
builder.output() << ">";
break;
default:
builder.output() << c;
}
}
}
void CfgViz( GraphVizBuilder& builder, const VarAddr& instr )
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
builder.output() << "VarAddr " << instr.varIndex();
}
void CfgViz( GraphVizBuilder& builder, const TempAddr& instr )
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
builder.output() << "TempAddr " << instr.tempIndex();
}
void CfgViz( GraphVizBuilder& builder, const Select& instr )
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
builder.output() << "Select " << instr.memberIndex();
}
void CfgViz( GraphVizBuilder& builder, const CreateTemporary& instr )
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
builder.output() << "CreateTemporary " << instr.index();
}
void CfgViz( GraphVizBuilder& builder, const GetTemporary& instr )
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
|
| ︙ | ︙ | |||
173 174 175 176 177 178 179 180 181 182 183 184 |
auto id = builder.getNodeId( pBB.get() );
GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );
builder.output() << "BB #" << id << ", " << index;
return true;
} );
}
void CfgViz( GraphVizBuilder& builder, const Ret& t )
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
| > > > > > > > > < < < < < < < < < < < < < < < | | < | 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
auto id = builder.getNodeId( pBB.get() );
GraphVizBuilder::Color col( builder, GraphVizBuilder::GetNodeColor( id ) );
builder.output() << "BB #" << id << ", " << index;
return true;
} );
}
void CfgViz( GraphVizBuilder& builder, const RetVoid& t )
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
builder.output() << "RetVoid";
}
void CfgViz( GraphVizBuilder& builder, const Ret& t )
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
builder.output() << "Ret";
}
void CfgViz( GraphVizBuilder& builder, const Not& instr )
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
|
| ︙ | ︙ | |||
220 221 222 223 224 225 226 |
builder.output() << "BinaryOp";
}
void CfgViz( GraphVizBuilder& builder, const Assert& instr )
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
| < < < < < < | < | < | > > > > > > > > | | 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
builder.output() << "BinaryOp";
}
void CfgViz( GraphVizBuilder& builder, const Assert& instr )
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
builder.output() << "Assert";
}
void CfgViz( GraphVizBuilder& builder, const Placeholder& instr )
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
builder.output() << '@' << instr.name();
}
void CfgViz( GraphVizBuilder& builder, const PHOverrideSet& instr )
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
builder.output() << "PHOverrideSet";
}
void CfgViz( GraphVizBuilder& builder, const PHOverrideClear& instr )
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
builder.output() << "PHOverrideClear";
}
void CfgViz( GraphVizBuilder& builder, const GhostCall& instr )
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
|
| ︙ | ︙ | |||
285 286 287 288 289 290 291 |
CfgViz( builder, t.trueDest().lock() );
CfgViz( builder, t.falseDest().lock() );
} );
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
| < | | | 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
CfgViz( builder, t.trueDest().lock() );
CfgViz( builder, t.falseDest().lock() );
} );
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
builder.output() << "CondBranch";
}
GraphVizBuilder::Row row( builder );
{
GraphVizBuilder::Cell cell( builder );
|
| ︙ | ︙ | |||
325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
CfgViz( builder, t.ghostCode().lock() );
CfgViz( builder, t.continuation().lock() );
} );
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
builder.output() << "GhostBranch";
}
GraphVizBuilder::Row row( builder );
{
| > | 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 |
CfgViz( builder, t.ghostCode().lock() );
CfgViz( builder, t.continuation().lock() );
} );
{
GraphVizBuilder::Row row( builder );
GraphVizBuilder::Cell cell( builder );
GraphVizBuilder::Color col( builder );
builder.output() << "GhostBranch";
}
GraphVizBuilder::Row row( builder );
{
|
| ︙ | ︙ |
Changes to bs/cir/cfgviz.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#ifndef GOOSE_CIR_CFGVIZ_H
#define GOOSE_CIR_CFGVIZ_H
namespace goose::cir
{
template< typename T >
void CfgViz( const char* pFilename, const T& x )
{
ofstream file( pFilename );
GraphVizBuilder builder( file, true );
CfgViz( builder, x );
}
extern void CfgViz( GraphVizBuilder& builder, const CFG& cfg );
extern void CfgViz( GraphVizBuilder& builder, const ptr< BasicBlock >& bb );
extern void CfgViz( GraphVizBuilder& builder, const eir::Value& val );
extern void CfgViz( GraphVizBuilder& builder, const Instruction& instr );
extern void CfgViz( GraphVizBuilder& builder, const Terminator& t );
extern void CfgViz( GraphVizBuilder& builder, const monostate& );
extern void CfgViz( GraphVizBuilder& builder, const Call& instr );
extern void CfgViz( GraphVizBuilder& builder, const VarAddr& instr );
extern void CfgViz( GraphVizBuilder& builder, const TempAddr& instr );
extern void CfgViz( GraphVizBuilder& builder, const Select& instr );
extern void CfgViz( GraphVizBuilder& builder, const CreateTemporary& instr );
extern void CfgViz( GraphVizBuilder& builder, const GetTemporary& instr );
extern void CfgViz( GraphVizBuilder& builder, const AllocVar& instr );
extern void CfgViz( GraphVizBuilder& builder, const Load& instr );
extern void CfgViz( GraphVizBuilder& builder, const Store& instr );
extern void CfgViz( GraphVizBuilder& builder, const Phi& instr );
extern void CfgViz( GraphVizBuilder& builder, const Not& instr );
extern void CfgViz( GraphVizBuilder& builder, const BinaryOp& instr );
extern void CfgViz( GraphVizBuilder& builder, const Assert& instr );
extern void CfgViz( GraphVizBuilder& builder, const Placeholder& instr );
| > > > > | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
#ifndef GOOSE_CIR_CFGVIZ_H
#define GOOSE_CIR_CFGVIZ_H
namespace goose::cir
{
template< typename T >
void CfgViz( const char* pFilename, const T& x )
{
ofstream file( pFilename );
GraphVizBuilder builder( file, true );
CfgViz( builder, x );
}
extern void CfgViz( GraphVizBuilder& builder, const Func& func );
extern void CfgViz( GraphVizBuilder& builder, const CFG& cfg );
extern void CfgViz( GraphVizBuilder& builder, const ptr< BasicBlock >& bb );
extern void CfgViz( GraphVizBuilder& builder, const eir::Value& val );
extern void CfgViz( GraphVizBuilder& builder, const InstrSeq& is );
extern void CfgViz( GraphVizBuilder& builder, const Instruction& instr );
extern void CfgViz( GraphVizBuilder& builder, const Terminator& t );
extern void CfgViz( GraphVizBuilder& builder, const monostate& );
extern void CfgViz( GraphVizBuilder& builder, const Call& instr );
extern void CfgViz( GraphVizBuilder& builder, const Constant& instr );
extern void CfgViz( GraphVizBuilder& builder, const VarAddr& instr );
extern void CfgViz( GraphVizBuilder& builder, const TempAddr& instr );
extern void CfgViz( GraphVizBuilder& builder, const Select& instr );
extern void CfgViz( GraphVizBuilder& builder, const CreateTemporary& instr );
extern void CfgViz( GraphVizBuilder& builder, const GetTemporary& instr );
extern void CfgViz( GraphVizBuilder& builder, const AllocVar& instr );
extern void CfgViz( GraphVizBuilder& builder, const Load& instr );
extern void CfgViz( GraphVizBuilder& builder, const Store& instr );
extern void CfgViz( GraphVizBuilder& builder, const Phi& instr );
extern void CfgViz( GraphVizBuilder& builder, const Not& instr );
extern void CfgViz( GraphVizBuilder& builder, const BinaryOp& instr );
extern void CfgViz( GraphVizBuilder& builder, const Assert& instr );
extern void CfgViz( GraphVizBuilder& builder, const Placeholder& instr );
extern void CfgViz( GraphVizBuilder& builder, const PHOverrideSet& instr );
extern void CfgViz( GraphVizBuilder& builder, const PHOverrideClear& instr );
extern void CfgViz( GraphVizBuilder& builder, const GhostCall& instr );
extern void CfgViz( GraphVizBuilder& builder, const RetVoid& t );
extern void CfgViz( GraphVizBuilder& builder, const Ret& t );
extern void CfgViz( GraphVizBuilder& builder, const Branch& t );
extern void CfgViz( GraphVizBuilder& builder, const CondBranch& t );
extern void CfgViz( GraphVizBuilder& builder, const GhostBranch& t );
extern void CfgViz( GraphVizBuilder& builder, const Break& t );
extern void CfgViz( GraphVizBuilder& builder, const Continue& t );
}
#endif
|
Changes to bs/cir/cir.h.
| ︙ | ︙ | |||
13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
static constexpr uint32_t InvalidVarId = numeric_limits< uint32_t >::max();
class CFG;
}
#include "helpers.h"
#include "varaddr.h"
#include "tempaddr.h"
#include "select.h"
#include "call.h"
#include "createtemporary.h"
#include "gettemporary.h"
#include "allocvar.h"
| > > > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
static constexpr uint32_t InvalidVarId = numeric_limits< uint32_t >::max();
class CFG;
}
#include "helpers.h"
#include "storagelocation.h"
#include "constant.h"
#include "varaddr.h"
#include "tempaddr.h"
#include "select.h"
#include "call.h"
#include "createtemporary.h"
#include "gettemporary.h"
#include "allocvar.h"
|
| ︙ | ︙ | |||
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | #include "ass.h" #include "placeholder.h" #include "phoverride.h" #include "ghostcall.h" #include "instruction.h" #include "terminator.h" #include "basicblock.h" #include "cfg.h" #include "func.h" #include "hash.h" #include "decorator.h" #include "cfgviz.h" #include "basicblock.inl" #endif | > | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | #include "ass.h" #include "placeholder.h" #include "phoverride.h" #include "ghostcall.h" #include "instruction.h" #include "terminator.h" #include "verifinstrfilter.h" #include "basicblock.h" #include "cfg.h" #include "func.h" #include "hash.h" #include "decorator.h" #include "cfgviz.h" #include "basicblock.inl" #endif |
Changes to bs/cir/comparison.h.
1 2 3 4 5 |
#ifndef GOOSE_CIR_COMPARISON_H
#define GOOSE_CIR_COMPARISON_H
namespace goose::cir
{
| | | | > | | | | > | | | | > | | | | > | | | | > | | | | > | | | | > | | | | > | | | | > | | | | > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
#ifndef GOOSE_CIR_COMPARISON_H
#define GOOSE_CIR_COMPARISON_H
namespace goose::cir
{
class Eq : public BinaryOp
{
public:
Eq( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const Eq& ins )
{
return out << "EQ";
}
};
class Neq : public BinaryOp
{
public:
Neq( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const Neq& ins )
{
return out << "NEQ";
}
};
class UGT : public BinaryOp
{
public:
UGT( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const UGT& ins )
{
return out << "UGT";
}
};
class UGE : public BinaryOp
{
public:
UGE( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const UGE& ins )
{
return out << "UGE";
}
};
class ULT : public BinaryOp
{
public:
ULT( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const ULT& ins )
{
return out << "ULT";
}
};
class ULE : public BinaryOp
{
public:
ULE( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const ULE& ins )
{
return out << "ULE";
}
};
class SGT : public BinaryOp
{
public:
SGT( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const SGT& ins )
{
return out << "SGT";
}
};
class SGE : public BinaryOp
{
public:
SGE( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const SGE& ins )
{
return out << "SGE";
}
};
class SLT : public BinaryOp
{
public:
SLT( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const SLT& ins )
{
return out << "SLT";
}
};
class SLE : public BinaryOp
{
public:
SLE( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const SLE& ins )
{
return out << "SLE";
}
};
}
#endif
|
Added bs/cir/constant.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#ifndef GOOSE_CIR_CONSTANT_H
#define GOOSE_CIR_CONSTANT_H
namespace goose::cir
{
class Constant : public BaseInstr< 0, true >
{
public:
template< typename V >
Constant( V&& val ) :
BaseInstr( val.locationId() ),
m_value( forward< V >( val ) )
{}
const auto& value() const { return m_value; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
bool haveSideEffects() const { return false; }
bool operator<( const Constant& rhs ) const
{
return m_value < rhs.m_value;
}
friend ostream& operator<<( ostream& out, const Constant& ins )
{
return out << "CONSTANT(" << ins.m_value << ')';
}
private:
eir::Value m_value;
};
}
#endif
|
Changes to bs/cir/createtemporary.h.
1 2 3 4 5 |
#ifndef GOOSE_CIR_CREATETEMPORARY_H
#define GOOSE_CIR_CREATETEMPORARY_H
namespace goose::cir
{
| | < | > | < < | < < < < | < | < < | < | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#ifndef GOOSE_CIR_CREATETEMPORARY_H
#define GOOSE_CIR_CREATETEMPORARY_H
namespace goose::cir
{
class CreateTemporary : public BaseInstr< 1, false >
{
public:
CreateTemporary( uint32_t index, LocationId loc ) :
BaseInstr( loc ),
m_index( index )
{}
const auto& index() const { return m_index; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
bool haveSideEffects() const { return true; }
bool operator<( const CreateTemporary& rhs ) const
{
return m_index < rhs.m_index;
}
friend ostream& operator<<( ostream& out, const CreateTemporary& ins )
{
return out << "CREATETEMP(" << ins.m_index << ')';
}
private:
uint32_t m_index = 0;
};
}
#endif
|
Changes to bs/cir/decorator.cpp.
1 2 3 4 5 6 |
#include "cir.h"
using namespace goose::cir;
void Decorator::appendTo( BasicBlock& bb ) &&
{
| < < | < < | < < | < | 1 2 3 4 5 6 7 8 9 10 |
#include "cir.h"
using namespace goose::cir;
void Decorator::appendTo( BasicBlock& bb ) &&
{
bb.append( move( m_prologue ) );
bb.append( move( m_operation ) );
bb.append( move( m_epilogue) );
}
|
Changes to bs/cir/decorator.h.
| ︙ | ︙ | |||
25 26 27 28 29 30 31 |
{
m_epilogue.emplace_back( forward< I >( instr ) );
}
void appendTo( BasicBlock& bb ) &&;
private:
| | | | | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
{
m_epilogue.emplace_back( forward< I >( instr ) );
}
void appendTo( BasicBlock& bb ) &&;
private:
InstrSeq m_prologue;
InstrSeq m_operation;
InstrSeq m_epilogue;
};
}
#endif
|
Changes to bs/cir/gettemporary.h.
1 2 3 4 5 |
#ifndef GOOSE_CIR_GETTEMPORARY_H
#define GOOSE_CIR_GETTEMPORARY_H
namespace goose::cir
{
| | | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#ifndef GOOSE_CIR_GETTEMPORARY_H
#define GOOSE_CIR_GETTEMPORARY_H
namespace goose::cir
{
class GetTemporary : public BaseInstr< 0, true >
{
public:
template< typename T >
GetTemporary( T&& type, uint32_t index, LocationId loc ) :
BaseInstr( loc ),
m_type( forward< T >( type ) ),
m_index( index )
{}
const auto& type() const { return m_type; }
const auto& index() const { return m_index; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
bool haveSideEffects() const { return false; }
bool operator<( const GetTemporary& rhs ) const
{
if( m_index != rhs.m_index )
return m_index < rhs.m_index;
return m_type < rhs.m_type;
}
|
| ︙ | ︙ |
Changes to bs/cir/ghostcall.h.
1 2 3 4 5 |
#ifndef GOOSE_CIR_GHOSTCALL_H
#define GOOSE_CIR_GHOSTCALL_H
namespace goose::cir
{
| | < | | | < | > < < | | | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#ifndef GOOSE_CIR_GHOSTCALL_H
#define GOOSE_CIR_GHOSTCALL_H
namespace goose::cir
{
class GhostCall : public BaseInstr< 1, true > // Can pop more depending on arg count
{
public:
GhostCall( uint32_t numArgs, LocationId loc ) :
BaseInstr( loc ),
m_numArgs( numArgs )
{}
const auto& numArgs() const { return m_numArgs; }
bool canBeExecuted() const { return false; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return true; }
bool operator<( const GhostCall& rhs ) const
{
return m_numArgs < rhs.m_numArgs;
}
friend ostream& operator<<( ostream& out, const GhostCall& ins )
{
return out << "GHOSTCALL";
}
private:
uint32_t m_numArgs = 0;
};
}
#endif
|
Changes to bs/cir/hash.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "cir/cir.h"
namespace std
{
size_t hash< goose::cir::Instruction >::operator()( const goose::cir::Instruction& x ) const
{
return goose::util::ComputeHash( x.content() );
}
size_t hash< goose::cir::Call >::operator()( const goose::cir::Call& x ) const
{
| > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
#include "cir/cir.h"
namespace std
{
size_t hash< goose::cir::InstrSeq >::operator()( const goose::cir::InstrSeq& is ) const
{
auto g = goose::util::ContainerHashGenerator( is );
return llvm::hash_combine_range( g.begin(), g.end() );
}
size_t hash< goose::cir::Instruction >::operator()( const goose::cir::Instruction& x ) const
{
return goose::util::ComputeHash( x.content() );
}
size_t hash< goose::cir::Call >::operator()( const goose::cir::Call& x ) const
{
return goose::util::ComputeHash( x.numArgs() );
}
size_t hash< goose::cir::Constant >::operator()( const goose::cir::Constant& x ) const
{
return goose::util::ComputeHash( x.value() );
}
size_t hash< goose::cir::VarAddr >::operator()( const goose::cir::VarAddr& x ) const
{
return llvm::hash_combine( goose::util::ComputeHash( x.varIndex() ), goose::util::ComputeHash( x.type() ) );
}
size_t hash< goose::cir::TempAddr >::operator()( const goose::cir::TempAddr& x ) const
{
return goose::util::ComputeHash( x.tempIndex() );
}
size_t hash< goose::cir::Select >::operator()( const goose::cir::Select& x ) const
{
return goose::util::ComputeHash( x.memberIndex() );
}
size_t hash< goose::cir::CreateTemporary >::operator()( const goose::cir::CreateTemporary& x ) const
{
return goose::util::ComputeHash( x.index() );
}
size_t hash< goose::cir::GetTemporary >::operator()( const goose::cir::GetTemporary& x ) const
{
return llvm::hash_combine( goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.index() ) );
}
size_t hash< goose::cir::AllocVar >::operator()( const goose::cir::AllocVar& x ) const
{
return llvm::hash_combine( goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.index() ) );
}
size_t hash< goose::cir::Load >::operator()( const goose::cir::Load& x ) const
{
return goose::util::ComputeHash( x.type() );
}
size_t hash< goose::cir::Store >::operator()( const goose::cir::Store& x ) const
{
return goose::util::ComputeHash( x.type() );
}
size_t hash< goose::cir::Phi >::operator()( const goose::cir::Phi& x ) const
{
// Note: we don't bother hashing the incomings here, and we don't really care because we only really need hashing for the
// predicates and phi doesn't make sense there
return llvm::hash_combine( goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.numIncomings() ), goose::util::ComputeHash( x.destIndex() ) );
}
size_t hash< goose::cir::Not >::operator()( const goose::cir::Not& x ) const
{
return 0;
}
size_t hash< goose::cir::And >::operator()( const goose::cir::And& x ) const
{
return 0;
}
size_t hash< goose::cir::Or >::operator()( const goose::cir::Or& x ) const
{
return 0;
}
size_t hash< goose::cir::Xor >::operator()( const goose::cir::Xor& x ) const
{
return 0;
}
size_t hash< goose::cir::Implies >::operator()( const goose::cir::Implies& x ) const
{
return 0;
}
size_t hash< goose::cir::Shl >::operator()( const goose::cir::Shl& x ) const
{
return 0;
}
size_t hash< goose::cir::LShr >::operator()( const goose::cir::LShr& x ) const
{
return 0;
}
size_t hash< goose::cir::AShr >::operator()( const goose::cir::AShr& x ) const
{
return 0;
}
size_t hash< goose::cir::Add >::operator()( const goose::cir::Add& x ) const
{
return 0;
}
size_t hash< goose::cir::Sub >::operator()( const goose::cir::Sub& x ) const
{
return 0;
}
size_t hash< goose::cir::Mul >::operator()( const goose::cir::Mul& x ) const
{
return 0;
}
size_t hash< goose::cir::UDiv >::operator()( const goose::cir::UDiv& x ) const
{
return 0;
}
size_t hash< goose::cir::SDiv >::operator()( const goose::cir::SDiv& x ) const
{
return 0;
}
size_t hash< goose::cir::URem >::operator()( const goose::cir::URem& x ) const
{
return 0;
}
size_t hash< goose::cir::SRem >::operator()( const goose::cir::SRem& x ) const
{
return 0;
}
size_t hash< goose::cir::Eq >::operator()( const goose::cir::Eq& x ) const
{
return 0;
}
size_t hash< goose::cir::Neq >::operator()( const goose::cir::Neq& x ) const
{
return 0;
}
size_t hash< goose::cir::UGT >::operator()( const goose::cir::UGT& x ) const
{
return 0;
}
size_t hash< goose::cir::UGE >::operator()( const goose::cir::UGE& x ) const
{
return 0;
}
size_t hash< goose::cir::ULT >::operator()( const goose::cir::ULT& x ) const
{
return 0;
}
size_t hash< goose::cir::ULE >::operator()( const goose::cir::ULE& x ) const
{
return 0;
}
size_t hash< goose::cir::SGT >::operator()( const goose::cir::SGT& x ) const
{
return 0;
}
size_t hash< goose::cir::SGE >::operator()( const goose::cir::SGE& x ) const
{
return 0;
}
size_t hash< goose::cir::SLT >::operator()( const goose::cir::SLT& x ) const
{
return 0;
}
size_t hash< goose::cir::SLE >::operator()( const goose::cir::SLE& x ) const
{
return 0;
}
size_t hash< goose::cir::Assert >::operator()( const goose::cir::Assert& x ) const
{
return 0;
}
size_t hash< goose::cir::Placeholder >::operator()( const goose::cir::Placeholder& x ) const
{
return llvm::hash_combine( goose::util::ComputeHash( x.type() ), goose::util::ComputeHash( x.name() ) );
}
size_t hash< goose::cir::PHOverrideSet >::operator()( const goose::cir::PHOverrideSet& x ) const
{
return goose::util::ComputeHash( x.name() );
}
size_t hash< goose::cir::PHOverrideClear >::operator()( const goose::cir::PHOverrideClear& x ) const
{
return goose::util::ComputeHash( x.name() );
}
size_t hash< goose::cir::GhostCall >::operator()( const goose::cir::GhostCall& x ) const
{
return goose::util::ComputeHash( x.numArgs() );
}
}
|
Changes to bs/cir/hash.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#ifndef GOOSE_CIR_HASH_H
#define GOOSE_CIR_HASH_H
namespace std
{
template<> struct hash< goose::cir::Instruction >
{
size_t operator()( const goose::cir::Instruction& x ) const;
};
template<> struct hash< goose::cir::Call >
{
size_t operator()( const goose::cir::Call& x ) const;
};
template<> struct hash< goose::cir::VarAddr >
{
size_t operator()( const goose::cir::VarAddr& x ) const;
};
template<> struct hash< goose::cir::TempAddr >
| > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#ifndef GOOSE_CIR_HASH_H
#define GOOSE_CIR_HASH_H
namespace std
{
template<> struct hash< goose::cir::InstrSeq >
{
size_t operator()( const goose::cir::InstrSeq& is ) const;
};
template<> struct hash< goose::cir::Instruction >
{
size_t operator()( const goose::cir::Instruction& x ) const;
};
template<> struct hash< goose::cir::Call >
{
size_t operator()( const goose::cir::Call& x ) const;
};
template<> struct hash< goose::cir::Constant >
{
size_t operator()( const goose::cir::Constant& x ) const;
};
template<> struct hash< goose::cir::VarAddr >
{
size_t operator()( const goose::cir::VarAddr& x ) const;
};
template<> struct hash< goose::cir::TempAddr >
|
| ︙ | ︙ | |||
189 190 191 192 193 194 195 |
};
template<> struct hash< goose::cir::Placeholder >
{
size_t operator()( const goose::cir::Placeholder& x ) const;
};
| | | > > > > > | 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
};
template<> struct hash< goose::cir::Placeholder >
{
size_t operator()( const goose::cir::Placeholder& x ) const;
};
template<> struct hash< goose::cir::PHOverrideSet >
{
size_t operator()( const goose::cir::PHOverrideSet& x ) const;
};
template<> struct hash< goose::cir::PHOverrideClear >
{
size_t operator()( const goose::cir::PHOverrideClear& x ) const;
};
template<> struct hash< goose::cir::GhostCall >
{
size_t operator()( const goose::cir::GhostCall& x ) const;
};
}
#endif
|
Changes to bs/cir/helpers.cpp.
1 2 3 4 5 6 7 8 9 |
#include "cir/cir.h"
namespace goose::cir
{
bool IsValueConstantOrExecutable( const eir::Value& val )
{
if( val.isConstant() )
return true;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > | > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
#include "cir/cir.h"
namespace goose::cir
{
void AppendInstrSeq( InstrSeq& is, InstrSeq&& isToAppend )
{
is.splice( is.end(), isToAppend );
// TODO update canBeExecuted, store it in InstrSeq?
}
void AppendInstrSeq( InstrSeq& is, const InstrSeq& isToAppend )
{
for( auto&& instr : isToAppend )
AppendToInstrSeq( is, instr );
}
void AppendValue( InstrSeq& is, Value&& v )
{
if( v.cir() )
AppendToInstrSeq( is, move( *v.cir() ) );
else
is.emplace_back( Constant( move( v ) ) );
}
void AppendValue( InstrSeq& is, const Value& v )
{
if( v.cir() )
AppendToInstrSeq( is, *v.cir() );
else
is.emplace_back( Constant( v ) );
}
bool IsValueConstantOrExecutable( const eir::Value& val )
{
if( val.isConstant() )
return true;
for( const auto& instr : *val.cir() )
{
if( !instr.canBeExecuted() )
return false;
}
return true;
}
bool CanValueBeEagerlyEvaluated( const eir::Value& val )
{
if( val.isConstant() )
return true;
for( const auto& instr : *val.cir() )
{
if( !instr.canBeEagerlyEvaluated() )
return false;
}
return true;
}
bool DoesInstrSeqHaveSideEffects( const InstrSeq& is )
{
for( auto&& instr : is )
{
if( instr.haveSideEffects() )
return true;
}
return false;
}
}
|
Changes to bs/cir/helpers.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_HELPERS_H
#define GOOSE_CIR_HELPERS_H
namespace goose::cir
{
class Instruction;
| > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > | > | > > > > > > > | > > > > > > > > > > > > > > | | > | | | | | < < | | | < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
#ifndef GOOSE_CIR_HELPERS_H
#define GOOSE_CIR_HELPERS_H
namespace goose::cir
{
class Instruction;
using InstrSeq = list< Instruction >;
extern bool IsValueConstantOrExecutable( const eir::Value& val );
extern bool CanValueBeEagerlyEvaluated( const eir::Value& val );
extern bool DoesInstrSeqHaveSideEffects( const InstrSeq& is );
extern void AppendInstrSeq( InstrSeq& is, InstrSeq&& isToAppend );
extern void AppendInstrSeq( InstrSeq& is, const InstrSeq& isToAppend );
extern void AppendValue( InstrSeq& is, Value&& v );
extern void AppendValue( InstrSeq& is, const Value& v );
template< typename I >
void AppendToInstrSeq( InstrSeq& is, I&& instr )
{
using II = remove_cvref_t< I >;
if constexpr( is_same_v< II, InstrSeq > )
{
AppendInstrSeq( is, forward< I >( instr ) );
}
else if constexpr( is_same_v< II, Value > )
{
AppendValue( is, forward< I >( instr ) );
}
else
{
is.emplace_back( Instruction( forward< I >( instr ) ) );
}
}
static inline void AppendToInstrSeq( InstrSeq& is, const ptr< InstrSeq >& instr )
{
AppendToInstrSeq( is, *instr );
}
static inline void AppendToInstrSeq( InstrSeq& is, ptr< InstrSeq >&& instr )
{
AppendToInstrSeq( is, move( *instr ) );
}
template< typename HI, typename... TI >
void AppendToInstrSeq( InstrSeq& is, HI&& headInstr, TI&&... tailInstrs )
{
AppendToInstrSeq( is, forward< HI >( headInstr ) );
AppendToInstrSeq( is, forward< TI >( tailInstrs )... );
}
template< typename T, typename... I >
auto BuildComputedValue( T&& type, I&&... instrs )
{
auto is = make_shared< InstrSeq >();
AppendToInstrSeq( *is, forward< I >( instrs )... );
return eir::Value( forward< T >( type ), move( is ) );
}
template< size_t popCount, bool pushesResult >
class BaseInstr
{
public:
BaseInstr( LocationId loc ) :
m_loc( loc )
{}
void setLocationId( LocationId loc ) { m_loc = loc; }
auto locationId() const { return m_loc; }
static constexpr size_t PopCount = popCount;
static constexpr bool PushesResult = pushesResult;
private:
LocationId m_loc;
};
template< typename T >
class TempStorage
{
public:
TempStorage( size_t size ) :
m_storage( size )
{}
template< typename TT >
T& set( uint32_t index, TT&& x )
{
if( index >= m_storage.size() )
m_storage.resize( index + 1 );
m_storage[index] = forward< TT >( x );
return m_storage[index];
}
const T* get( uint32_t index ) const
{
if( index < m_storage.size() )
return &m_storage[index];
return nullptr;
}
T* get( uint32_t index )
{
if( index < m_storage.size() )
return &m_storage[index];
return nullptr;
}
private:
llvm::SmallVector< T, 16 > m_storage;
};
}
#endif
|
Changes to bs/cir/instruction.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "cir/cir.h"
namespace goose::cir
{
bool Instruction::canBeExecuted() const
{
return visit( []< typename ET >( const ET& e )
{
if constexpr( is_same_v< ET, monostate > )
return false;
| < < < < > > > > > > > > > > > < < | < < < < | < < < < | < < < < < | | | | | < | < < | < < | > | | > | | < | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
#include "cir/cir.h"
namespace goose::cir
{
bool Instruction::canBeExecuted() const
{
return visit( []< typename ET >( const ET& e )
{
if constexpr( is_same_v< ET, monostate > )
return false;
else
return e.canBeExecuted();
}, m_content );
}
bool Instruction::canBeEagerlyEvaluated() const
{
return visit( []< typename ET >( const ET& e )
{
if constexpr( is_same_v< ET, monostate > )
return false;
else
return e.canBeEagerlyEvaluated();
}, m_content );
}
bool Instruction::haveSideEffects() const
{
return visit( []< typename ET >( const ET& e )
{
if constexpr( is_same_v< ET, monostate > )
return false;
else
return e.haveSideEffects();
}, m_content );
}
ostream& operator<<( ostream& out, const Instruction& inst )
{
return visit( [&]< typename ET >( const ET& e ) -> ostream&
{
if constexpr( is_same_v< ET, monostate > )
return out << "NIL";
else
return out << e;
}, inst.m_content );
}
ostream& operator<<( ostream& out, const Select& ins )
{
out << "SELECT(" << ins.m_memberIndex;
return out << ')';
}
ostream& operator<<( ostream& out, const Load& ins )
{
out << "LOAD(";
return out << ins.m_type << ')';
}
ostream& operator<<( ostream& out, const Store& ins )
{
return out << "STORE";
}
ostream& operator<<( ostream& out, const PHOverrideSet& ins )
{
return out << "PHOVERRIDESET(" << ins.m_name << ')';
}
ostream& operator<<( ostream& out, const PHOverrideClear& ins )
{
return out << "PHOVERRIDECLEAR(" << ins.m_name << ')';
}
bool Select::operator<( const Select& rhs ) const
{
return m_memberIndex < rhs.m_memberIndex;
}
bool Load::operator<( const Load& rhs ) const
{
return m_type < rhs.m_type;
}
bool Store::operator<( const Store& rhs ) const
{
return m_type < rhs.m_type;
}
bool PHOverrideSet::operator<( const PHOverrideSet& rhs ) const
{
return m_name < rhs.m_name;
}
bool PHOverrideClear::operator<( const PHOverrideClear& rhs ) const
{
return m_name < rhs.m_name;
}
}
|
Changes to bs/cir/instruction.h.
| ︙ | ︙ | |||
21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
Instruction( TempAddr&& tad ) :
m_content( move( tad ) )
{}
Instruction( Select&& sel ) :
m_content( move( sel ) )
{}
Instruction( CreateTemporary&& ct ) :
m_content( move( ct ) )
{}
Instruction( GetTemporary&& gt ) :
m_content( move( gt ) )
| > > > > | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
Instruction( TempAddr&& tad ) :
m_content( move( tad ) )
{}
Instruction( Select&& sel ) :
m_content( move( sel ) )
{}
Instruction( Constant&& cst ) :
m_content( move( cst ) )
{}
Instruction( CreateTemporary&& ct ) :
m_content( move( ct ) )
{}
Instruction( GetTemporary&& gt ) :
m_content( move( gt ) )
|
| ︙ | ︙ | |||
154 155 156 157 158 159 160 |
m_content( move( x ) )
{}
Instruction( Placeholder&& x ) :
m_content( move( x ) )
{}
| | > > > > > | 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
m_content( move( x ) )
{}
Instruction( Placeholder&& x ) :
m_content( move( x ) )
{}
Instruction( PHOverrideSet&& x ) :
m_content( move( x ) )
{}
Instruction( PHOverrideClear&& x ) :
m_content( move( x ) )
{}
Instruction( GhostCall&& x ) :
m_content( move( x ) )
{}
using Content = variant
<
monostate,
Call,
VarAddr,
TempAddr,
Select,
Constant,
CreateTemporary,
GetTemporary,
AllocVar,
Load,
Store,
Phi,
|
| ︙ | ︙ | |||
210 211 212 213 214 215 216 |
SGT,
SGE,
SLT,
SLE,
Assert,
GhostCall,
| > | < < < < > | 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
SGT,
SGE,
SLT,
SLE,
Assert,
GhostCall,
PHOverrideSet,
PHOverrideClear,
Placeholder
>;
const auto& content() const { return m_content; }
bool canBeExecuted() const;
bool canBeEagerlyEvaluated() const;
bool haveSideEffects() const;
friend ostream& operator<<( ostream& out, const Instruction& inst );
bool operator<( const Instruction& rhs ) const
{
return m_content < rhs.m_content;
}
|
| ︙ | ︙ |
Changes to bs/cir/load.h.
1 2 3 4 5 |
#ifndef GOOSE_CIR_LOAD_H
#define GOOSE_CIR_LOAD_H
namespace goose::cir
{
| | | | | < > < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#ifndef GOOSE_CIR_LOAD_H
#define GOOSE_CIR_LOAD_H
namespace goose::cir
{
class Load : public BaseInstr< 1, true >
{
public:
template< typename T >
Load( T&& type, LocationId loc ) :
BaseInstr( loc ),
m_type( forward< T >( type ) )
{}
const auto& type() const { return m_type; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
bool haveSideEffects() const { return false; }
bool operator<( const Load& rhs ) const;
friend ostream& operator<<( ostream& out, const Load& ins );
private:
eir::Term m_type;
};
}
#endif
|
Changes to bs/cir/logic.h.
1 2 3 4 5 6 7 8 |
#ifndef GOOSE_CIR_LOGIC_H
#define GOOSE_CIR_LOGIC_H
namespace goose::cir
{
class And : public BinaryOp
{
public:
| | | > | | | > | | | > | | | > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
#ifndef GOOSE_CIR_LOGIC_H
#define GOOSE_CIR_LOGIC_H
namespace goose::cir
{
class And : public BinaryOp
{
public:
And( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const And& ins )
{
return out << "AND";
}
};
class Or : public BinaryOp
{
public:
Or( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const Or& ins )
{
return out << "OR";
}
};
class Xor : public BinaryOp
{
public:
Xor( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const Xor& ins )
{
return out << "XOR";
}
};
// Logical implication (to be used only for verification expression)
class Implies : public BinaryOp
{
public:
Implies( LocationId loc ) :
BinaryOp( loc )
{}
friend ostream& operator<<( ostream& out, const Implies& ins )
{
return out << "IMPLIES";
}
};
}
#endif
|
Changes to bs/cir/loopaddrs.cpp.
1 2 3 4 |
#include "cir.h"
namespace goose::cir
{
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | < < < < < | < < < | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
#include "cir.h"
namespace goose::cir
{
// EvalStores is a tiny CIR InstrSeq evaluator that only considers store instructions
// to addresses whose path can be calculated entirely at compilation time
class AddrEvalStack
{
public:
template< typename T >
void push( T&& x )
{
m_stack.push( forward< T >( x ) );
}
bool pop()
{
if( m_stack.empty() )
return false;
m_stack.pop();
return true;
}
template< typename T >
optional< T > pop()
{
if( m_stack.empty() )
return nullopt;
if( !holds_alternative< T >( m_stack.top() ) )
return nullopt;
optional< T > result = move( get< T >( m_stack.top() ) );
m_stack.pop();
return result;
}
bool empty() const { return m_stack.empty(); }
private:
stack< variant< monostate, StorageLocation, Value > > m_stack;
};
template< typename I, typename F >
bool EvalStores( AddrEvalStack& stack, const I& instr, F&& storeHandler )
{
// For all the instructions we don't care about here,
// push/pop mock values on the stack to keep the stack layout
// as intended by the instruction sequence.
for( size_t i = 0; i < I::PopCount; ++i )
{
if( !stack.pop() )
return false;
}
if( I::PushesResult )
stack.push( monostate() );
return true;
}
template< typename F >
bool EvalStores( AddrEvalStack& stack, const monostate&, F&& storeHandler )
{
return true;
}
template< typename F >
bool EvalStores( AddrEvalStack& stack, const Constant& instr, F&& storeHandler )
{
stack.push( instr.value() );
return true;
}
template< typename F >
bool EvalStores( AddrEvalStack& stack, const Call& instr, F&& storeHandler )
{
if( !stack.pop() )
return false;
for( size_t i = 0; i < instr.numArgs(); ++i )
{
if( !stack.pop() )
return false;
}
stack.push( monostate() );
return true;
}
template< typename F >
bool EvalStores( AddrEvalStack& stack, const GhostCall& instr, F&& storeHandler )
{
auto gfunc = stack.pop< Value >();
if( !gfunc )
return false;
auto argCount = instr.numArgs();
GhostFuncApplication gfa( move( *gfunc ), instr.locationId() );
auto& args = gfa.args();
args.resize( argCount );
for( size_t i = 0; i < argCount; ++i )
{
variant< Value, Address > arg;
auto a = stack.pop< Value >();
if( a )
arg = move( *a );
else
{
auto a = stack.pop< StorageLocation >();
if( !a || holds_alternative< Address >( *a ) )
return false;
arg = move( get< Address >( *a ) );
}
args[argCount - i - 1] = move( arg );
}
stack.push( StorageLocation { move( gfa ) } );
return true;
}
template< typename F >
bool EvalStores( AddrEvalStack& stack, const VarAddr& instr, F&& storeHandler )
{
stack.push( Address( Address::Origin::Stack, instr.varIndex(), instr.locationId() ) );
return true;
}
template< typename F >
bool EvalStores( AddrEvalStack& stack, const TempAddr& instr, F&& storeHandler )
{
if( !stack.pop() )
return false;
stack.push( Address( Address::Origin::Stack, instr.tempIndex(), instr.locationId() ) );
return true;
}
template< typename F >
bool EvalStores( AddrEvalStack& stack, const Select& instr, F&& storeHandler )
{
if( stack.empty() )
return false;
auto baseAddr = stack.pop< StorageLocation >();
if( !baseAddr )
{
// Not an error, just means the base address comes from an indrection,
// or a function call and we don't handle this case here.
//
// TODO:
// Later we might either outright emit an error right here if this occurs
// inside of a loop because it means we can't verify it and risk a false
// positive. Or we could try and go the extra mile to represent
// the indirection in a way that can be verified.
return true;
}
if( !holds_alternative< Address >( *baseAddr ) )
return false;
stack.push( StorageLocation{
Address::Select( move( get< Address >( *baseAddr ) ),
instr.memberIndex(), instr.locationId() ) } );
return true;
}
template< typename F >
bool EvalStores( AddrEvalStack& stack, const Store& instr, F&& storeHandler )
{
if( !stack.pop() )
return false;
if( stack.empty() )
return false;
auto sloc = stack.pop< StorageLocation >();
if( !sloc )
{
// Not an error, see comment in the function above
return true;
}
storeHandler( instr.type(), *sloc );
return true;
}
template< typename F >
bool EvalStores( AddrEvalStack& stack, const Instruction& instr, F&& storeHandler )
{
return visit( [&]( auto&& i )
{
return EvalStores( stack, i, forward< F >( storeHandler ) );
}, instr.content() );
}
template< typename F >
bool EvalStores( const BasicBlock& bb, F&& storeHandler )
{
AddrEvalStack stack;
for( auto&& instr : bb.instructions() )
{
if( !EvalStores( stack, instr, storeHandler ) )
return false;
}
return true;
}
void MarkSLocChangedByLoop( const ptr< CFG >& cfg, uint32_t loopId, const eir::Term& type, const StorageLocation& sloc )
{
cfg->setStorageLocationModifiedByLoop( loopId, type, sloc );
const auto& pHeader = cfg->getBB( loopId );
if( pHeader->loopId() )
MarkSLocChangedByLoop( cfg, pHeader->loopId(), type, sloc );
}
void ComputeLoopModifiedAddrs( const ptr< CFG >& cfg )
{
cfg->forEachBB( [&]( auto&& bb )
{
if( !bb->loopId() )
return;
EvalStores( *bb, [&]( auto&& type, auto&& sloc )
{
MarkSLocChangedByLoop( cfg, bb->loopId(), type, sloc );
} );
} );
}
}
|
Changes to bs/cir/meson.build.
1 2 3 4 5 6 |
goose_cir = library( 'goose-cir',
'cfg.cpp',
'dominators.cpp',
'loops.cpp',
'loopaddrs.cpp',
'instruction.cpp',
| < < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
goose_cir = library( 'goose-cir',
'cfg.cpp',
'dominators.cpp',
'loops.cpp',
'loopaddrs.cpp',
'instruction.cpp',
'terminator.cpp',
'func.cpp',
'helpers.cpp',
'verifinstrfilter.cpp',
'hash.cpp',
'decorator.cpp',
'cfgviz.cpp',
include_directories: bsinc,
dependencies: [tracy_dep]
)
|
| ︙ | ︙ |
Changes to bs/cir/not.h.
1 2 3 4 5 |
#ifndef GOOSE_CIR_NOT_H
#define GOOSE_CIR_NOT_H
namespace goose::cir
{
| | < | < > < < | < < < < | < | < | | < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#ifndef GOOSE_CIR_NOT_H
#define GOOSE_CIR_NOT_H
namespace goose::cir
{
class Not : public BaseInstr< 1, true >
{
public:
Not( LocationId loc ) :
BaseInstr( loc )
{}
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
bool haveSideEffects() const { return false; }
bool operator<( const Not& rhs ) const
{
return false;
}
friend ostream& operator<<( ostream& out, const Not& ins )
{
return out << "NOT";
}
};
}
#endif
|
Changes to bs/cir/phi.h.
1 2 3 4 5 6 7 |
#ifndef GOOSE_CIR_PHI_H
#define GOOSE_CIR_PHI_H
namespace goose::cir
{
class BasicBlock;
| | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#ifndef GOOSE_CIR_PHI_H
#define GOOSE_CIR_PHI_H
namespace goose::cir
{
class BasicBlock;
class Phi : public BaseInstr< 0, false >
{
public:
template< typename T >
Phi( T&& type, uint32_t numIncomings, uint32_t destIndex, LocationId loc ) :
BaseInstr( loc ),
m_type( forward< T >( type ) ),
m_destIndex( destIndex )
{
m_incomings.reserve( numIncomings );
}
const auto& type() const { return m_type; }
|
| ︙ | ︙ | |||
33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
if( !func( bb.lock(), val ) )
return;
}
}
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
bool operator<( const Phi& rhs ) const
{
if( m_type != rhs.m_type )
return m_type < rhs.m_type;
return m_destIndex < rhs.m_destIndex;
| > | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
if( !func( bb.lock(), val ) )
return;
}
}
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
bool haveSideEffects() const { return true; }
bool operator<( const Phi& rhs ) const
{
if( m_type != rhs.m_type )
return m_type < rhs.m_type;
return m_destIndex < rhs.m_destIndex;
|
| ︙ | ︙ |
Changes to bs/cir/phoverride.h.
1 2 3 4 5 |
#ifndef GOOSE_CIR_PHOVERRIDE_H
#define GOOSE_CIR_PHOVERRIDE_H
namespace goose::cir
{
| | < | | | > | < < > > > | > > > > > > > > > > > > > > > > > > > > | > | | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
#ifndef GOOSE_CIR_PHOVERRIDE_H
#define GOOSE_CIR_PHOVERRIDE_H
namespace goose::cir
{
// A pseudo instruction that defines an override value for a placeholder
class PHOverrideSet : public BaseInstr< 1, false >
{
public:
template< typename S >
PHOverrideSet( S&& name, LocationId loc ) :
BaseInstr( loc ),
m_name( forward< S >( name ) )
{}
const auto& name() const { return m_name; }
// This can't be executed, it's only meant to appear
// in expressions handled by the verifier.
bool canBeExecuted() const { return false; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return false; }
bool operator<( const PHOverrideSet& rhs ) const;
friend ostream& operator<<( ostream& out, const PHOverrideSet& ins );
private:
StringId m_name;
};
// A pseudo instruction that clears an override value for a placeholder
class PHOverrideClear : public BaseInstr< 0, 0 >
{
public:
template< typename S >
PHOverrideClear( S&& name, LocationId loc ) :
BaseInstr( loc ),
m_name( forward< S >( name ) )
{}
const auto& name() const { return m_name; }
// This can't be executed, it's only meant to appear
// in expressions handled by the verifier.
bool canBeExecuted() const { return false; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return false; }
bool operator<( const PHOverrideClear& rhs ) const;
friend ostream& operator<<( ostream& out, const PHOverrideClear& ins );
private:
StringId m_name;
};
}
#endif
|
Changes to bs/cir/placeholder.h.
1 2 3 4 5 |
#ifndef GOOSE_CIR_PLACEHOLDER_H
#define GOOSE_CIR_PLACEHOLDER_H
namespace goose::cir
{
| | | | | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#ifndef GOOSE_CIR_PLACEHOLDER_H
#define GOOSE_CIR_PLACEHOLDER_H
namespace goose::cir
{
// A placeholder value with a name, to be replaced with something else.
// Intended to represent the @ return value placeholder in functions'
// post conditions.
class Placeholder : public BaseInstr< 0, true >
{
public:
template< typename T, typename S >
Placeholder( T&& type, S&& name, LocationId loc ) :
BaseInstr( loc ),
m_type( forward< T >( type ) ),
m_name( forward< S >( name ) )
{}
const auto& type() const { return m_type; }
const auto& name() const { return m_name; }
// This can't be executed, it's only meant to appear
// in expressions handled by the verifier.
bool canBeExecuted() const { return false; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return false; }
bool operator<( const Placeholder& rhs ) const
{
if( m_name != rhs.m_name )
return m_name < rhs.m_name;
return m_type < rhs.m_type;
}
|
| ︙ | ︙ |
Changes to bs/cir/ret.h.
1 2 3 4 5 |
#ifndef GOOSE_CIR_RET_H
#define GOOSE_CIR_RET_H
namespace goose::cir
{
| | | | < < < | > | | > | > > | < > | < > > > > | > > > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#ifndef GOOSE_CIR_RET_H
#define GOOSE_CIR_RET_H
namespace goose::cir
{
class RetVoid
{
public:
RetVoid( LocationId loc ) :
m_loc( loc )
{}
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex ) {}
const auto& locationId() const { return m_loc; }
private:
LocationId m_loc;
};
class Ret
{
public:
Ret( LocationId loc ) :
m_loc( loc )
{}
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
void addCFGEdges( const ptr< CFG >& cfg, uint32_t srcBBIndex ) {}
const auto& locationId() const { return m_loc; }
private:
LocationId m_loc;
};
}
#endif
|
Changes to bs/cir/select.h.
1 2 3 4 5 |
#ifndef GOOSE_CIR_SELECT_H
#define GOOSE_CIR_SELECT_H
namespace goose::cir
{
| | < | | < < < < < > < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#ifndef GOOSE_CIR_SELECT_H
#define GOOSE_CIR_SELECT_H
namespace goose::cir
{
class Select : public BaseInstr< 1, true >
{
public:
Select( uint32_t memberIndex, LocationId loc ) :
BaseInstr( loc ),
m_memberIndex( memberIndex )
{}
uint32_t memberIndex() const
{
return m_memberIndex;
}
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return false; }
bool operator<( const Select& rhs ) const;
friend ostream& operator<<( ostream& out, const Select& ins );
private:
uint32_t m_memberIndex = 0;
};
}
#endif
|
Added bs/cir/storagelocation.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
#ifndef GOOSE_CIR_STORAGELOCATION_H
#define GOOSE_CIR_STORAGELOCATION_H
namespace goose::cir
{
using SelectPath = llvm::SmallVector< uint32_t, 4 >;
class Address
{
public:
enum class Origin
{
Stack,
Heap
};
Address() {}
Address( Address&& ) = default;
Address( const Address& ) = default;
Address& operator=( Address&& ) = default;
Address& operator=( const Address& ) = default;
Address( Origin o, uint32_t originIndex, LocationId loc ) :
m_origin( o ),
m_loc( loc )
{
m_path.emplace_back( originIndex );
}
auto origin() const { return m_origin; }
const auto& path() const { return m_path; }
auto locationId() const { return m_loc; }
static Address Select( Address&& baseAddr, uint32_t index, LocationId loc )
{
baseAddr.m_path.emplace_back( index );
baseAddr.m_loc = loc;
return baseAddr;
}
bool operator<( const Address& rhs ) const
{
if( m_origin != rhs.m_origin )
return m_origin < rhs.m_origin;
return m_path < rhs.m_path;
}
private:
SelectPath m_path;
Origin m_origin = Origin::Stack;
LocationId m_loc;
};
class GhostFuncApplication
{
public:
GhostFuncApplication( cir::Value&& func, LocationId loc ) :
m_func( move( func ) ),
m_loc( loc )
{}
const auto& func() const { return m_func; }
const auto& args() const { return m_args; }
auto& args() { return m_args; }
auto locationId() const { return m_loc; }
bool operator<( const GhostFuncApplication& rhs ) const
{
if( m_func != rhs.m_func )
return m_func < rhs.m_func;
return m_args < rhs.m_args;
}
private:
eir::Value m_func;
llvm::SmallVector< variant< eir::Value, Address >, 4 > m_args;
LocationId m_loc;
};
using StorageLocation = variant< Address, GhostFuncApplication, monostate >;
}
#endif
|
Changes to bs/cir/store.h.
1 2 3 4 5 6 7 8 |
#ifndef GOOSE_CIR_STORE_H
#define GOOSE_CIR_STORE_H
namespace goose::cir
{
class Store
{
public:
| | | < < > < | | < > | | | | < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#ifndef GOOSE_CIR_STORE_H
#define GOOSE_CIR_STORE_H
namespace goose::cir
{
class Store
{
public:
template< typename T >
Store( T&& type, eir::LocationId srcLocId, eir::LocationId destLocId ) :
m_type( forward< T >( type ) ),
m_srcLocId( srcLocId ),
m_destLocId( destLocId )
{}
const auto& type() const { return m_type; }
const auto& srcLocId() const { return m_srcLocId; }
const auto& destLocId() const { return m_destLocId; }
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return true; }
bool haveSideEffects() const { return true; }
static constexpr size_t PopCount = 2;
static constexpr bool PushesResult = false;
bool operator<( const Store& rhs ) const;
friend ostream& operator<<( ostream& out, const Store& ins );
private:
eir::Term m_type;
eir::LocationId m_srcLocId = 0;
eir::LocationId m_destLocId = 0;
};
}
#endif
|
Changes to bs/cir/tempaddr.h.
1 2 3 4 5 |
#ifndef GOOSE_CIR_TEMPADDR_H
#define GOOSE_CIR_TEMPADDR_H
namespace goose::cir
{
| | < | < > < < < < < > < | < | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
#ifndef GOOSE_CIR_TEMPADDR_H
#define GOOSE_CIR_TEMPADDR_H
namespace goose::cir
{
class TempAddr : public BaseInstr< 1, true >
{
public:
TempAddr( uint32_t i, LocationId loc ) :
BaseInstr( loc ),
m_tempIndex( i )
{}
uint32_t tempIndex() const
{
return m_tempIndex;
}
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return false; }
bool operator<( const TempAddr& rhs ) const
{
return m_tempIndex < rhs.m_tempIndex;
}
friend ostream& operator<<( ostream& out, const TempAddr& ins )
{
return out << "TEMPADDR(" << ins.m_tempIndex << ')';
}
private:
uint32_t m_tempIndex = 0;
};
}
#endif
|
Changes to bs/cir/terminator.h.
1 2 3 4 5 6 7 8 9 10 |
#ifndef GOOSE_CIR_TERMINATOR_H
#define GOOSE_CIR_TERMINATOR_H
namespace goose::cir
{
class BasicBlock;
class Terminator
{
public:
| | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#ifndef GOOSE_CIR_TERMINATOR_H
#define GOOSE_CIR_TERMINATOR_H
namespace goose::cir
{
class BasicBlock;
class Terminator
{
public:
Terminator( RetVoid&& r ) :
m_content( move( r ) )
{}
Terminator( Ret&& r ) :
m_content( move( r ) )
{}
Terminator( Branch&& b ) :
m_content( move( b ) )
|
| ︙ | ︙ | |||
32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
Terminator( Continue&& c ) :
m_content( move( c ) )
{}
using Content = variant
<
Ret,
Branch,
CondBranch,
GhostBranch,
Break,
Continue
>;
| > | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
Terminator( Continue&& c ) :
m_content( move( c ) )
{}
using Content = variant
<
RetVoid,
Ret,
Branch,
CondBranch,
GhostBranch,
Break,
Continue
>;
|
| ︙ | ︙ |
Changes to bs/cir/tests/dom-1.cpp.
| ︙ | ︙ | |||
23 24 25 26 27 28 29 |
auto bb2 = cfg->createBB();
auto bb3 = cfg->createBB();
auto bb4 = cfg->createBB();
auto bb5 = cfg->createBB();
auto bb6 = cfg->createBB();
bb1->setTerminator( Branch( bb2 ) );
| | | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
auto bb2 = cfg->createBB();
auto bb3 = cfg->createBB();
auto bb4 = cfg->createBB();
auto bb5 = cfg->createBB();
auto bb6 = cfg->createBB();
bb1->setTerminator( Branch( bb2 ) );
bb2->setTerminator( CondBranch( bb3, bb4 ) );
bb3->setTerminator( Branch( bb5 ) );
bb4->setTerminator( Branch( bb5 ) );
bb5->setTerminator( CondBranch( bb2, bb6 ) );
ComputeDominators( cfg );
THEN( "We get the expected result" )
{
const auto& idoms = cfg->idoms();
|
| ︙ | ︙ |
Changes to bs/cir/tests/dom-2.cpp.
| ︙ | ︙ | |||
29 30 31 32 33 34 35 |
auto bb8 = cfg->createBB();
auto bb9 = cfg->createBB();
auto bba = cfg->createBB();
auto bbb = cfg->createBB();
auto bbc = cfg->createBB();
auto bbd = cfg->createBB();
| | | | | | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
auto bb8 = cfg->createBB();
auto bb9 = cfg->createBB();
auto bba = cfg->createBB();
auto bbb = cfg->createBB();
auto bbc = cfg->createBB();
auto bbd = cfg->createBB();
bb1->setTerminator( CondBranch( bb2, bb3 ) );
bb2->setTerminator( CondBranch( bb4, bb7 ) );
bb4->setTerminator( Branch( bb5 ) );
bb5->setTerminator( Branch( bb6 ) );
bb6->setTerminator( Branch( bb8 ) );
bb7->setTerminator( Branch( bb8 ) );
bb8->setTerminator( CondBranch( bb7, bbd ) );
bb3->setTerminator( CondBranch( bb9, bba ) );
bba->setTerminator( CondBranch( bbb, bbc ) );
bb9->setTerminator( Branch( bbc ) );
bbb->setTerminator( Branch( bbc ) );
bbc->setTerminator( Branch( bbd ) );
ComputeDominators( cfg );
THEN( "We get the expected result" )
|
| ︙ | ︙ |
Changes to bs/cir/tests/meson.build.
| ︙ | ︙ | |||
15 16 17 18 19 20 21 |
goose_builtins,
goose_sema,
goose_lex,
goose_parse,
goose_diagnostics,
goose_execute,
goose_codegen,
| < | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
goose_builtins,
goose_sema,
goose_lex,
goose_parse,
goose_diagnostics,
goose_execute,
goose_codegen,
goose_compile
],
include_directories: bsinc,
dependencies: [catch2_dep, llvm_dep, lld_deps, tracy_dep]
)
test( 'cir-' + t, exe )
endforeach
|
Changes to bs/cir/varaddr.h.
1 2 3 4 5 |
#ifndef GOOSE_CIR_VARADDR_H
#define GOOSE_CIR_VARADDR_H
namespace goose::cir
{
| | | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#ifndef GOOSE_CIR_VARADDR_H
#define GOOSE_CIR_VARADDR_H
namespace goose::cir
{
class VarAddr : public BaseInstr< 0, true >
{
public:
template< typename T >
VarAddr( uint32_t varIndex, T&& type, LocationId loc ) :
BaseInstr( loc ),
m_type( forward< T >( type ) ),
m_varIndex( varIndex )
{}
const auto& type() const { return m_type; }
uint32_t varIndex() const
{
return m_varIndex;
}
bool canBeExecuted() const { return true; }
bool canBeEagerlyEvaluated() const { return false; }
bool haveSideEffects() const { return false; }
bool operator<( const VarAddr& rhs ) const
{
if( m_varIndex != rhs.m_varIndex )
return m_varIndex < rhs.m_varIndex;
return m_type < rhs.m_type;
|
| ︙ | ︙ |
Added bs/cir/verifinstrfilter.cpp.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
#include "cir/cir.h"
namespace goose::cir
{
void VerifInstrFilter::push( int32_t instrCount )
{
m_stack.push( instrCount );
}
int32_t VerifInstrFilter::pop()
{
if( m_stack.empty() )
return 0;
auto result = m_stack.top();
m_stack.pop();
return result;
}
void VerifInstrFilter::retire()
{
auto count = pop();
auto end = m_pendingInstrs.end();
auto begin = end - abs( count );
if( count < 0 )
{
m_discardCount -= count;
for( auto it = begin; it != end; ++it )
m_instrDiscardFlags[*it] = true;
}
m_pendingInstrs.erase( begin, end );
}
bool VerifInstrFilter::processInstr( const Call& instr )
{
size_t count = 1;
auto c = pop();
if( c <= 0 )
return false;
count += c;
for( uint32_t i = 0; i < instr.numArgs(); ++i )
{
auto c = pop();
if( c <= 0 )
return false;
count += c;
}
push( count );
return true;
}
bool VerifInstrFilter::processInstr( const GhostCall& instr )
{
size_t count = 1;
auto c = pop();
if( c == 0 )
return false;
count += abs( c );
for( uint32_t i = 0; i < instr.numArgs(); ++i )
{
auto c = pop();
if( c == 0 )
return false;
count += abs( c );
}
push( -count );
return true;
}
bool VerifInstrFilter::processInstr( const Store& instr )
{
size_t count = 1;
bool isVerifCode = false;
auto c = pop();
if( c == 0 )
return false;
if( c < 0 )
isVerifCode = true;
count += abs( c );
c = pop();
if( c == 0 )
return false;
if( c < 0 )
isVerifCode = true;
count += abs( c );
push( isVerifCode ? -count : count );
retire();
return true;
}
bool VerifInstrFilter::processInstr( const Assert& instr )
{
size_t count = 1;
auto c = pop();
if( c == 0 )
return false;
count += abs( c );
push( -count );
retire();
return true;
}
bool VerifInstrFilter::processInstr( const Placeholder& instr )
{
push( -1 );
retire();
return true;
}
bool VerifInstrFilter::processInstr( const PHOverrideSet& instr )
{
size_t count = 1;
auto c = pop();
if( c == 0 )
return false;
count += abs( c );
push( -count );
retire();
return true;
}
bool VerifInstrFilter::processInstr( const PHOverrideClear& instr )
{
push( -1 );
retire();
return true;
}
template< typename T >
bool VerifInstrFilter::processInstr( const T& instr )
{
size_t count = 1;
bool isVerifCode = false;
for( uint32_t i = 0; i < T::PopCount; ++i )
{
auto c = pop();
if( !c )
return false;
if( c < 0 )
isVerifCode = true;
count += abs( c );
}
if( T::PushesResult )
push( isVerifCode ? -count : count );
return true;
}
bool VerifInstrFilter::processInstr( const Instruction& instr )
{
m_pendingInstrs.emplace_back( m_instrCount++ );
return visit( [&]< typename T >( const T& instr )
{
if constexpr( is_same_v< T, monostate > )
return false;
else
return VerifInstrFilter::processInstr( instr );
}, instr.content() );
}
}
|
Added bs/cir/verifinstrfilter.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
#ifndef GOOSE_CIR_VERIFINSTRFILTER_H
#define GOOSE_CIR_VERIFINSTRFILTER_H
namespace goose::cir
{
struct VerifInstrFilter
{
public:
VerifInstrFilter( size_t inputInstrCount ) :
m_instrDiscardFlags( inputInstrCount )
{
m_pendingInstrs.reserve( inputInstrCount );
}
bool processInstr( const Instruction& instr );
template< typename T >
void filter( const InstrSeq& input, T& output )
{
assert( input.size() == m_instrDiscardFlags.size() );
output.reserve( input.size() - m_discardCount );
auto fit = m_instrDiscardFlags.begin();
for( const auto& instr : input )
{
if( !*fit )
output.emplace_back( instr );
++fit;
}
}
private:
void push( int32_t instrCount );
int32_t pop();
void retire();
bool processInstr( const Call& instr );
bool processInstr( const GhostCall& instr );
bool processInstr( const Store& instr );
bool processInstr( const Assert& instr );
bool processInstr( const Placeholder& instr );
bool processInstr( const PHOverrideSet& instr );
bool processInstr( const PHOverrideClear& instr );
template< typename T >
bool processInstr( const T& instr );
llvm::SmallVector< size_t, 16 > m_pendingInstrs;
vector< bool > m_instrDiscardFlags;
stack< int32_t > m_stack;
size_t m_instrCount = 0;
size_t m_discardCount = 0;
};
template< typename T >
bool FilterVerificationInstructions( const InstrSeq& is, T& destination )
{
VerifInstrFilter f( is.size() );
for( const auto& instr : is )
{
if( !f.processInstr( instr ) )
return false;
}
f.filter( is, destination );
return true;
}
}
#endif
|
Changes to bs/codegen/address.cpp.
1 2 3 4 5 6 7 | #include "codegen.h" #include "builtins/builtins.h" using namespace goose; using namespace goose::codegen; using namespace goose::builtins; | < < < | < < < | < < | < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
#include "codegen.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;
llvm::Value* codegen::AddressToGEP( llvm::IRBuilder<>& builder, const codegen::Address& addr )
{
if( addr.path().empty() )
return addr.originAddr();
llvm::SmallVector< llvm::Value*, 8 > idxs;
idxs.push_back( llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), 0 ) );
for( auto it = addr.path().rbegin(); it != addr.path().rend(); ++it )
idxs.push_back( llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), *it ) );
return builder.CreateGEP( addr.originAddr()->getType()->getScalarType()->getPointerElementType(), addr.originAddr(), idxs );
}
bool Module::buildInstruction( State& st, const VarAddr& va )
{
auto* ppVal = st.temporaries->get( va.varIndex() );
assert( ppVal );
if( !ppVal )
return false;
st.stack.push( Address( *ppVal ) );
return true;
}
bool Module::buildInstruction( State& st, const TempAddr& ta )
{
auto initVal = st.stack.pop( m_llvmBuilder );
if( !initVal )
return false;
auto* ppVal = st.temporaries->get( ta.tempIndex() );
if( !ppVal )
{
st.temporaries->set( ta.tempIndex(), *initVal );
ppVal = st.temporaries->get( ta.tempIndex() );
}
if( llvm::isa< llvm::AllocaInst >( **ppVal ) )
{
st.stack.push( Address( *ppVal ) );
return true;
}
auto* pAlloca = buildAlloca( st, ( *ppVal )->getType() );
m_llvmBuilder.CreateStore( *ppVal, pAlloca );
st.temporaries->set( ta.tempIndex(), pAlloca );
st.stack.push( Address( pAlloca ) );
return true;
}
bool Module::buildInstruction( State& st, const Select& s )
{
auto baseAddr = st.stack.pop();
if( !baseAddr )
return false;
if( holds_alternative< Address >( *baseAddr ) )
st.stack.push( Address::Select( move( get< Address >( *baseAddr ) ), s.memberIndex() ) );
else
st.stack.push( Address::Select( Address( get< llvm::Value* >( *baseAddr ) ), s.memberIndex() ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::GhostCall& gc )
{
// We should never encounter this during codegen, since it's a construct only
// intended to be used in verification expressions.
return false;
}
|
Added bs/codegen/address.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#ifndef GOOSE_CODEGEN_ADDRESS_H
#define GOOSE_CODEGEN_ADDRESS_H
namespace goose::codegen
{
class Address
{
public:
Address( llvm::Value* originAddr ) :
m_originAddr( originAddr )
{}
auto originAddr() const { return m_originAddr; }
const auto& path() const { return m_path; }
static Address Select( Address&& baseAddr, uint32_t index )
{
baseAddr.m_path.emplace_back( index );
return baseAddr;
}
private:
SelectPath m_path;
llvm::Value* m_originAddr = nullptr;
};
extern llvm::Value* AddressToGEP( llvm::IRBuilder<>& builder, const codegen::Address& addr );
}
#endif
|
Changes to bs/codegen/arithops.cpp.
1 2 3 4 5 6 7 | #include "codegen.h" #include "builtins/builtins.h" using namespace goose; using namespace goose::codegen; using namespace goose::builtins; | | | | | | | | | > | | | | | | | | > | | | | | | | | > | | | | > | < | | | > | | | | | | | | > > > > | < < < | | | | | | > | | | | | | | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
#include "codegen.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;
bool Module::buildInstruction( State& st, const cir::Add& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateAdd( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::Sub& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateSub( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::Mul& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateMul( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::UDiv& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateUDiv( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::SDiv& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateSDiv( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::URem& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateURem( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::SRem& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateSRem( *lhs, *rhs ) );
return true;
}
|
Changes to bs/codegen/basicblock.cpp.
1 2 3 4 5 6 | #include "codegen.h" #include "builtins/builtins.h" using namespace goose; using namespace goose::codegen; | | | > | | > > > > > | > | > | > > | > > | > | | | | > > > > > > > > > > > > > > > > > > | | | | < < | | | | > > | | | | > | | | | | > > | < | | | > | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
#include "codegen.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::codegen;
llvm::BasicBlock* Module::GetOrCreateLLVMBB( State& st, const ptr< cir::BasicBlock >& pBB )
{
if( !pBB->llvmBB() )
pBB->setLLVMBB( llvm::BasicBlock::Create( GetLLVMContext(), "", st.llvmFunc ) );
return pBB->llvmBB();
}
llvm::BasicBlock* Module::buildBasicBlock( State& st, const ptr< BasicBlock >& pBB )
{
auto pLLVMBB = GetOrCreateLLVMBB( st, pBB );
if( pLLVMBB->getTerminator() || pBB->codeGenStarted() )
return pLLVMBB;
m_llvmBuilder.SetInsertPoint( pLLVMBB );
auto pCurrentBB = pBB;
for(;;)
{
pCurrentBB->setCodeGenStarted();
const auto* pInstrs = pCurrentBB->runnableInstructions();
if( !pInstrs )
return nullptr;
for( auto&& instr : *pInstrs )
{
if( !buildInstruction( st, instr ) )
return nullptr;
}
// If the terminator is a ghostbranch, directly
// append the content of the continuation block
// to avoid unecessary basic blocks splits whenever
// some ghost code is present
if( !pCurrentBB->terminator() )
break;
auto* pGBranch = get_if< GhostBranch >( &pCurrentBB->terminator()->content() );
if( !pGBranch )
break;
pCurrentBB = pGBranch->continuation().lock();
}
if( !buildTerminator( st, *pCurrentBB->terminator() ) )
return nullptr;
return pLLVMBB;
}
bool Module::buildTerminator( State& st, const cir::Terminator& terminator )
{
return visit( [&]( auto&& e )
{
return buildTerminator( st, e );
}, terminator.content() );
}
bool Module::buildTerminator( State& st, const cir::RetVoid& r )
{
m_llvmBuilder.CreateRetVoid();
return true;
}
bool Module::buildTerminator( State& st, const cir::Ret& r )
{
auto retVal = st.stack.pop( m_llvmBuilder );
if( !retVal )
return false;
m_llvmBuilder.CreateRet( *retVal );
return true;
}
bool Module::buildTerminator( State& st, const cir::Branch& b )
{
auto pLLVMBB = GetOrCreateLLVMBB( st, b.dest().lock() );
m_llvmBuilder.CreateBr( pLLVMBB );
llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );
if( !buildBasicBlock( st, b.dest().lock() ) )
return false;
return true;
}
bool Module::buildTerminator( State& st, const cir::CondBranch& cb )
{
auto condVal = st.stack.pop( m_llvmBuilder );
if( !condVal )
return false;
auto pTrueLLVMBB = GetOrCreateLLVMBB( st, cb.trueDest().lock() );
auto pFalseLLVMBB = GetOrCreateLLVMBB( st, cb.falseDest().lock() );
m_llvmBuilder.CreateCondBr( *condVal, pTrueLLVMBB, pFalseLLVMBB );
llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );
if( !buildBasicBlock( st, cb.trueDest().lock() ) )
return false;
if( !buildBasicBlock( st, cb.falseDest().lock() ) )
return false;
return true;
}
bool Module::buildTerminator( State& st, const cir::GhostBranch& gb )
{
auto pLLVMBB = GetOrCreateLLVMBB( st, gb.continuation().lock() );
m_llvmBuilder.CreateBr( pLLVMBB );
llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );
if( !buildBasicBlock( st, gb.continuation().lock() ) )
return false;
return true;
}
|
Changes to bs/codegen/codegen.h.
| ︙ | ︙ | |||
23 24 25 26 27 28 29 30 31 32 |
using namespace cir;
using namespace sema;
extern llvm::LLVMContext& GetLLVMContext();
extern optional< string > Mangle( const Term& identity );
}
#include "module.h"
#endif
| > > | 23 24 25 26 27 28 29 30 31 32 33 34 |
using namespace cir;
using namespace sema;
extern llvm::LLVMContext& GetLLVMContext();
extern optional< string > Mangle( const Term& identity );
}
#include "address.h"
#include "stack.h"
#include "module.h"
#endif
|
Changes to bs/codegen/compareops.cpp.
1 2 3 4 5 6 7 | #include "codegen.h" #include "builtins/builtins.h" using namespace goose; using namespace goose::codegen; using namespace goose::builtins; | | | | | | | | | > | | | | | | | | > | | | | > | < | | | > | | | | | | | | > | | | | | | | | > | | | | | | | | > | | | | | | | | > | | | | | | | | > | | | | | | | | > | | | | | | | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
#include "codegen.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;
bool Module::buildInstruction( State& st, const cir::Eq& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateICmpEQ( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::Neq& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateICmpNE( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::UGT& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateICmpUGT( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::UGE& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateICmpUGE( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::ULT& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateICmpULT( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::ULE& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateICmpULE( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::SGT& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateICmpSGT( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::SGE& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateICmpSGE( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::SLT& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateICmpSLT( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::SLE& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateICmpSLE( *lhs, *rhs ) );
return true;
}
|
Changes to bs/codegen/func.cpp.
| ︙ | ︙ | |||
45 46 47 48 49 50 51 |
pllvmFunc = buildFuncProto( c, func, name, linkageType );
if( !pllvmFunc )
return nullptr;
if( func.isExternal() )
return pllvmFunc;
| > | > | | | | | | | < < < < < < | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
pllvmFunc = buildFuncProto( c, func, name, linkageType );
if( !pllvmFunc )
return nullptr;
if( func.isExternal() )
return pllvmFunc;
const auto& cfg = func.cir()->body();
State st( c, cfg->temporariesCount(), pllvmFunc );
// Generate allocas and stores for the args
auto pEntryBB = cfg->entryBB();
if( !pEntryBB->llvmBB() )
pEntryBB->setLLVMBB( llvm::BasicBlock::Create( GetLLVMContext(), "", pllvmFunc ) );
st.allocaBasicBlock = pEntryBB->llvmBB();
m_llvmBuilder.SetInsertPoint( pEntryBB->llvmBB() );
llvm::SmallVector< NullInit< llvm::Value* >, 8 > args;
for( auto&& arg : pllvmFunc->args() )
{
st.lastEmittedAlloca = m_llvmBuilder.CreateAlloca( arg.getType() );
args.push_back( st.lastEmittedAlloca );
}
size_t i = 0;
for( auto&& arg : pllvmFunc->args() )
{
m_llvmBuilder.CreateStore( &arg, args[i] );
st.temporaries->set( i, args[i] );
++i;
}
if( !buildCFG( st, pllvmFunc, func.cir()->body() ) )
return nullptr;
llvm::verifyFunction( *pllvmFunc );
return pllvmFunc;
}
llvm::BasicBlock* Module::buildCFG( State& st, llvm::Function* llvmFunc, const ptr< cir::CFG >& pCFG )
{
return buildBasicBlock( st, pCFG->entryBB() );
}
|
Changes to bs/codegen/instructions.cpp.
1 2 3 4 5 6 7 | #include "codegen.h" #include "builtins/builtins.h" using namespace goose; using namespace goose::codegen; using namespace goose::builtins; | > > > > > > > > > > > | | | | | | | > | < < < | | < | < < | | | < < > < | | < | | > > > | | < < < | < < < < | | < | > | | < | < > | | > | | | > > | | | | | | | | | | | | | | > | | > > > > | | < | > | | | | > | > | | | | | > | > | | | | | | | | > | > > | > | > > > | | | | | | | > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
#include "codegen.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;
bool Module::buildInstruction( State& st, const cir::InstrSeq& is )
{
for( auto&& instr : is )
{
if( !buildInstruction( st, instr ) )
return false;
}
return true;
}
bool Module::buildInstruction( State& st, const cir::Instruction& instr )
{
return visit( [&]< typename T >( T&& inst )
{
return buildInstruction( st, inst );
}, instr.content() );
}
bool Module::buildInstruction( State& st, const monostate& )
{
return false;
}
bool Module::buildInstruction( State& st, const cir::Call& call )
{
auto callee = st.stack.pop< llvm::Function* >( m_llvmBuilder );
if( !callee )
return false;
llvm::SmallVector< llvm::Value*, 8 > args;
args.resize( call.numArgs() );
for( uint32_t i = 0; i < call.numArgs(); ++i )
{
auto parg = st.stack.pop( m_llvmBuilder );
if( !parg )
return false;
args[call.numArgs() - i - 1] = *parg;
}
st.stack.push( m_llvmBuilder.CreateCall( llvm::FunctionCallee( *callee ), args ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::Constant& ct )
{
auto val = buildValue( st, ct.value() );
if( !val )
return false;
st.stack.push( val );
return true;
}
bool Module::buildInstruction( State& st, const cir::CreateTemporary& ct )
{
auto initVal = st.stack.pop( m_llvmBuilder );
if( !initVal )
return false;
createTemporary( st, ct.index(), *initVal );
return true;
}
bool Module::buildInstruction( State& st, const cir::GetTemporary& gt )
{
auto* ppVal = st.temporaries->get( gt.index() );
assert( ppVal );
if( !ppVal )
return false;
st.stack.push( *ppVal );
return true;
}
llvm::Value* Module::createTemporary( State& st, uint32_t index, llvm::Value* pValue ) const
{
st.temporaries->set( index, pValue );
return pValue;
}
llvm::Value* Module::buildAlloca( State& st, llvm::Type* type )
{
llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );
if( st.lastEmittedAlloca && st.lastEmittedAlloca->getNextNode() )
m_llvmBuilder.SetInsertPoint( st.lastEmittedAlloca->getNextNode() );
else
m_llvmBuilder.SetInsertPoint( st.allocaBasicBlock, st.allocaBasicBlock->begin() );
st.lastEmittedAlloca = m_llvmBuilder.CreateAlloca( type );
return st.lastEmittedAlloca;
}
bool Module::buildInstruction( State& st, const cir::AllocVar& av )
{
auto type = LowerTypeForRuntime( st.context, av.type() );
if( !type )
return false;
auto* pAlloca = buildAlloca( st, GetLLVMType( *type ) );
createTemporary( st, av.index(), pAlloca );
return true;
}
bool Module::buildInstruction( State& st, const cir::Load& load )
{
auto ptrVal = st.stack.pop( m_llvmBuilder );
if( !ptrVal )
return false;
auto type = LowerTypeForRuntime( st.context, *EIRToValue( load.type() ) );
if( !type )
return false;
auto* llvmType = GetLLVMType( *type );
assert( llvmType );
st.stack.push( m_llvmBuilder.CreateLoad( llvmType, *ptrVal ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::Store& store )
{
auto val = st.stack.pop( m_llvmBuilder );
if( !val )
return false;
auto ptrVal = st.stack.pop( m_llvmBuilder );
if( !ptrVal )
return false;
if( llvm::isa< llvm::ConstantAggregate >( *val ) )
{
stringstream name;
name << ".constaggr" << hex << m_nextAggregateID++;
auto pGlob = new llvm::GlobalVariable( m_llvmModule,
( *val )->getType(), true, llvm::GlobalValue::LinkageTypes::PrivateLinkage,
static_cast< llvm::Constant* >( *val ), name.str() );
auto size = m_dataLayout.getTypeAllocSize( ( *val )->getType() );
m_llvmBuilder.CreateMemCpy( *ptrVal, llvm::None, pGlob, llvm::None,
llvm::ConstantInt::get( llvm::Type::getInt32Ty( GetLLVMContext() ), size ) );
return true;
}
m_llvmBuilder.CreateStore( *val, *ptrVal );
return true;
}
bool Module::buildInstruction( State& st, const cir::Phi& p )
{
auto type = LowerTypeForRuntime( st.context, p.type() );
if( !type )
return false;
auto* pPHI = m_llvmBuilder.CreatePHI( GetLLVMType( *type ), p.numIncomings() );
if( !pPHI )
return false;
{
llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );
p.forAllIncomings( [&]( auto&& bb, auto&& val )
{
auto pIncomingBB = buildBasicBlock( st, bb );
if( !pIncomingBB )
{
pPHI = nullptr;
return false;
}
auto* pVal = buildValue( st, val );
if( !pVal )
{
pPHI = nullptr;
return false;
}
pPHI->addIncoming( pVal, pIncomingBB );
return true;
} );
}
if( !pPHI )
return false;
createTemporary( st, p.destIndex(), pPHI );
return true;
}
bool Module::buildInstruction( State& st, const cir::Assert& ass )
{
// We should never encounter this during codegen, since it's a construct only
// intended to be used in verification expressions.
return false;
}
bool Module::buildInstruction( State& st, const cir::Placeholder& ph )
{
// We should never encounter this during codegen, since it's a construct only
// intended to be used in verification expressions.
return false;
}
bool Module::buildInstruction( State& st, const cir::Implies& bo )
{
// We should never encounter this during codegen, since it's a construct only
// intended to be used in verification expressions.
return false;
}
bool Module::buildInstruction( State& st, const cir::PHOverrideSet& phos )
{
// We should never encounter this during codegen, since it's a construct only
// intended to be used in verification expressions.
return false;
}
bool Module::buildInstruction( State& st, const cir::PHOverrideClear& phoc )
{
// We should never encounter this during codegen, since it's a construct only
// intended to be used in verification expressions.
return false;
}
|
Changes to bs/codegen/logicops.cpp.
1 2 3 4 5 6 7 | #include "codegen.h" #include "builtins/builtins.h" using namespace goose; using namespace goose::codegen; using namespace goose::builtins; | | | | | > | | | | | | | | > | | | | | | | | > | | | | | | | | > | | | | | | | | > | < < < | < | > > > > | > | | | | | | | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
#include "codegen.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;
bool Module::buildInstruction( State& st, const cir::Not& uo )
{
auto operand = st.stack.pop( m_llvmBuilder );
if( !operand )
return false;
st.stack.push( m_llvmBuilder.CreateXor( *operand, llvm::ConstantInt::getTrue( GetLLVMContext() ) ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::And& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateAnd( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::Or& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateOr( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::Xor& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateXor( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::Shl& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateShl( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::LShr& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateLShr( *lhs, *rhs ) );
return true;
}
bool Module::buildInstruction( State& st, const cir::AShr& bo )
{
auto rhs = st.stack.pop( m_llvmBuilder );
if( !rhs )
return false;
auto lhs = st.stack.pop( m_llvmBuilder );
if( !lhs )
return false;
st.stack.push( m_llvmBuilder.CreateAShr( *lhs, *rhs ) );
return true;
}
|
Changes to bs/codegen/module.h.
| ︙ | ︙ | |||
25 26 27 28 29 30 31 |
llvm::Function* getOrCreateFunc( const Context& c, const builtins::Func& func, const string& name,
llvm::Function::LinkageTypes linkageType );
void runOptimizationPasses();
bool emitToFile( const string& filename, llvm::CodeGenFileType type );
private:
| | > | > > > > > | > > > > | | | | | > | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | > | | | | | | | | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
llvm::Function* getOrCreateFunc( const Context& c, const builtins::Func& func, const string& name,
llvm::Function::LinkageTypes linkageType );
void runOptimizationPasses();
bool emitToFile( const string& filename, llvm::CodeGenFileType type );
private:
struct State
{
State( const Context& c, size_t numVars, llvm::Function* f ) :
context( c ),
llvmFunc( f ),
temporaries( make_shared< storage_type >( numVars ) )
{}
const Context& context;
llvm::Function* llvmFunc = nullptr;
llvm::BasicBlock* allocaBasicBlock = nullptr;
llvm::AllocaInst* lastEmittedAlloca = nullptr;
using storage_type = cir::TempStorage< NullInit< llvm::Value* > >;
ptr< storage_type > temporaries;
Stack stack;
};
static llvm::BasicBlock* GetOrCreateLLVMBB( State& st, const ptr< cir::BasicBlock >& pBB );
llvm::Function* getCurrentFunction() const
{
return m_llvmBuilder.GetInsertBlock()->getParent();
}
llvm::BasicBlock* buildCFG( State& st, llvm::Function* llvmFunc, const ptr< cir::CFG >& pCFG );
llvm::BasicBlock* buildBasicBlock( State& st, const ptr< cir::BasicBlock >& pBB );
llvm::Value* buildValue( State& st, const Value& val );
llvm::Constant* buildConstant( State& st, const Value& val );
llvm::Constant* buildConstantPointer( State& st, const builtins::PointerType& ptType, const Value& v );
bool buildInstruction( State& st, const cir::InstrSeq& is );
bool buildInstruction( State& st, const cir::Instruction& instr );
bool buildInstruction( State& st, const monostate& );
bool buildInstruction( State& st, const cir::Call& call );
bool buildInstruction( State& st, const cir::Constant& ct );
bool buildInstruction( State& st, const cir::VarAddr& va );
bool buildInstruction( State& st, const cir::TempAddr& ta );
bool buildInstruction( State& st, const cir::Select& s );
bool buildInstruction( State& st, const cir::CreateTemporary& ct );
bool buildInstruction( State& st, const cir::GetTemporary& gt );
bool buildInstruction( State& st, const cir::AllocVar& av );
bool buildInstruction( State& st, const cir::Load& load );
bool buildInstruction( State& st, const cir::Store& store );
bool buildInstruction( State& st, const cir::Phi& p );
bool buildInstruction( State& st, const cir::Not& uo );
bool buildInstruction( State& st, const cir::And& bo );
bool buildInstruction( State& st, const cir::Or& bo );
bool buildInstruction( State& st, const cir::Xor& bo );
bool buildInstruction( State& st, const cir::Implies& bo );
bool buildInstruction( State& st, const cir::Shl& bo );
bool buildInstruction( State& st, const cir::LShr& bo );
bool buildInstruction( State& st, const cir::AShr& bo );
bool buildInstruction( State& st, const cir::Add& bo );
bool buildInstruction( State& st, const cir::Sub& bo );
bool buildInstruction( State& st, const cir::Mul& bo );
bool buildInstruction( State& st, const cir::UDiv& bo );
bool buildInstruction( State& st, const cir::SDiv& bo );
bool buildInstruction( State& st, const cir::URem& bo );
bool buildInstruction( State& st, const cir::SRem& bo );
bool buildInstruction( State& st, const cir::Eq& bo );
bool buildInstruction( State& st, const cir::Neq& bo );
bool buildInstruction( State& st, const cir::UGT& bo );
bool buildInstruction( State& st, const cir::UGE& bo );
bool buildInstruction( State& st, const cir::ULT& bo );
bool buildInstruction( State& st, const cir::ULE& bo );
bool buildInstruction( State& st, const cir::SGT& bo );
bool buildInstruction( State& st, const cir::SGE& bo );
bool buildInstruction( State& st, const cir::SLT& bo );
bool buildInstruction( State& st, const cir::SLE& bo );
bool buildInstruction( State& st, const cir::Assert& ass );
bool buildInstruction( State& st, const cir::Placeholder& ph );
bool buildInstruction( State& st, const cir::PHOverrideSet& phos );
bool buildInstruction( State& st, const cir::PHOverrideClear& phoc );
bool buildInstruction( State& st, const cir::GhostCall& gc );
bool buildTerminator( State& st, const cir::Terminator& terminator );
bool buildTerminator( State& st, const cir::RetVoid& r );
bool buildTerminator( State& st, const cir::Ret& r );
bool buildTerminator( State& st, const cir::Branch& b );
bool buildTerminator( State& st, const cir::CondBranch& cb );
bool buildTerminator( State& st, const cir::GhostBranch& gb );
template< typename T >
bool buildTerminator( State& st, const T& t )
{
return false;
}
llvm::Value* buildAlloca( State& st, llvm::Type* type );
llvm::Value* createTemporary( State& st, uint32_t index, llvm::Value* pValue ) const;
llvm::Module m_llvmModule;
llvm::DataLayout m_dataLayout;
llvm::IRBuilder<> m_llvmBuilder;
llvm::TargetMachine* m_targetMachine = nullptr;
unordered_map< string, llvm::GlobalVariable* > m_strings;
|
| ︙ | ︙ |
Added bs/codegen/stack.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
#ifndef GOOSE_CODEGEN_STACK_H
#define GOOSE_CODEGEN_STACK_H
namespace goose::codegen
{
class Stack
{
public:
using Slot = variant< llvm::Value*, codegen::Address >;
template< typename T = llvm::Value* >
optional< T > pop( llvm::IRBuilder<>& builder )
{
if( m_stack.empty() )
return nullopt;
auto result = m_stack.top();
m_stack.pop();
if constexpr( is_same_v< T, codegen::Address > )
{
if( !holds_alternative< codegen::Address >( result ) )
return nullopt;
return get< codegen::Address >( result );
}
else if constexpr( is_same_v< T, llvm::Value* > )
{
if( holds_alternative< codegen::Address >( result ) )
return AddressToGEP( builder, get< codegen::Address >( result ) );
else
return get< llvm::Value* >( result );
}
else if( holds_alternative< llvm::Value* >( result ) )
return llvm::dyn_cast_or_null< remove_pointer_t< T > >( get< llvm::Value* >( result ) );
return nullopt;
}
optional< Slot > pop()
{
if( m_stack.empty() )
return nullopt;
auto result = m_stack.top();
m_stack.pop();
return result;
}
template< typename T >
void push( T&& v )
{
m_stack.push( forward< T >( v ) );
}
private:
stack< Slot > m_stack;
};
}
#endif
|
Changes to bs/codegen/value.cpp.
1 2 3 4 5 6 7 | #include "codegen.h" #include "builtins/builtins.h" using namespace goose; using namespace goose::codegen; using namespace goose::builtins; | | | | > | > > > > > > | | | | > | > > | > > > > > > > > > > > > > > > > > > > > > > > | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
#include "codegen.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::codegen;
using namespace goose::builtins;
llvm::Value* Module::buildValue( State& st, const Value& val )
{
if( val.isPoison() )
return nullptr;
if( val.isConstant() )
return buildConstant( st, val );
if( !buildInstruction( st, *val.cir() ) )
return nullptr;
auto result = st.stack.pop( m_llvmBuilder );
if( !result )
return nullptr;
return *result;
}
llvm::Constant* Module::buildConstant( State& st, const Value& v )
{
assert( v.isConstant() );
auto val = LowerConstantForRuntime( st.context, v );
if( !val )
return nullptr;
if( auto b = FromValue< bool >( *val ) )
{
return *b ?
llvm::ConstantInt::getTrue( GetLLVMContext() ) :
llvm::ConstantInt::getFalse( GetLLVMContext() );
}
else if( auto intVal = FromValue< APSInt >( *val ) )
{
return llvm::ConstantInt::get( GetLLVMType( *EIRToValue( val->type() ) ),
*FromValue< APSInt >( *val ) );
}
else if( auto ptType = FromValue< PointerType >( *EIRToValue( val->type() ) ) )
{
return buildConstantPointer( st, *ptType, *val );
}
else if( auto recType = FromValue< RecordType >( *EIRToValue( val->type() ) ) )
{
llvm::SmallVector< llvm::Constant*, 8 > members;
assert( holds_alternative< pvec >( val->val() ) );
bool success = true;
ForEachInVectorTerm( val->val(), [&]( auto&& t )
{
auto* pMemberVal = buildConstant( st, *EIRToValue( t ) );
if( !pMemberVal )
{
success = false;
return false;
}
members.push_back( pMemberVal );
return true;
} );
if( !success )
return nullptr;
return llvm::ConstantStruct::get( static_cast< llvm::StructType* >
( GetLLVMType( *EIRToValue( val->type() ) ) ), members );
}
else if( auto funcType = FromValue< FuncType >( *EIRToValue( val->type() ) ) )
{
if( IsBuiltinFunc( *val ) || IsBuiltinIntrinsicFunc( *val ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage(
val->locationId(), "builtin functions can't be called at runtime." );
return nullptr;
}
// If it is a plain function, we just need to generate or retrieve it.
if( val->isConstant() )
{
auto f = FromValue< builtins::Func >( *val );
if( !f )
return nullptr;
llvm::IRBuilderBase::InsertPointGuard g( m_llvmBuilder );
return getOrCreateFunc( st.context, *f );
}
else
{
// TODO: function pointers
assert( false );
return nullptr;
}
}
DiagnosticsManager::GetInstance().emitErrorMessage(
v.locationId(), "constants with compile-time types are not supported by code generation.", 0 );
return nullptr;
}
llvm::Constant* Module::buildConstantPointer( State& st, const PointerType& ptType, const Value& v )
{
// We currently have two types of pointer constants: nullptr,
// and pointers to string literals.
if( auto str = get_if< string >( &v.val() ) )
{
llvm::GlobalVariable* pGlob = nullptr;
|
| ︙ | ︙ |
Changes to bs/compile/compiler.cpp.
| ︙ | ︙ | |||
82 83 84 85 86 87 88 |
optional< Value > Compiler::LoadAndExecuteFile( const ptr< Env >& e, const string& filename, const Term& identity,
const Term& returnType, optional< Value > defRetVal )
{
ProfileZoneScoped;
ProfileZoneName( filename.c_str(), filename.size() );
auto cfg = LoadAndParseFile( e, filename, identity, returnType, defRetVal );
| | | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
optional< Value > Compiler::LoadAndExecuteFile( const ptr< Env >& e, const string& filename, const Term& identity,
const Term& returnType, optional< Value > defRetVal )
{
ProfileZoneScoped;
ProfileZoneName( filename.c_str(), filename.size() );
auto cfg = LoadAndParseFile( e, filename, identity, returnType, defRetVal );
if( !cfg || cfg->isPoisoned() )
return PoisonValue();
if( !cfg->entryBB()->canBeExecuted() )
{
DiagnosticsManager::GetInstance().emitErrorMessage( 0,
format( "{}: can not be executed.", filename ) );
return PoisonValue();
}
execute::VM vm;
return vm.execute( *cfg );
}
ptr< cir::CFG > Compiler::LoadAndParseFile( const ptr< Env >& e, const string& filename, const Term& identity,
const Term& returnType, optional< Value > defRetVal )
{
ProfileZoneScoped;
ProfileZoneName( filename.c_str(), filename.size() );
|
| ︙ | ︙ | |||
146 147 148 149 150 151 152 |
if( cfg->currentBB() && !cfg->currentBB()->terminator() )
{
p.flushValue();
cb->destroyAllLiveValues( c );
if( returnType == GetValueType< void >() )
| | | 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
if( cfg->currentBB() && !cfg->currentBB()->terminator() )
{
p.flushValue();
cb->destroyAllLiveValues( c );
if( returnType == GetValueType< void >() )
cfg->emitTerminator( r->currentLocation(), cir::RetVoid( r->currentLocation() ) );
else if( !defRetVal )
{
dm.emitSyntaxErrorMessage( r->currentLocation(), "missing return statement." );
return nullptr;
}
else
{
|
| ︙ | ︙ | |||
171 172 173 174 175 176 177 |
dm.emitErrorMessage( defRetVal->locationId(), "ambiguous default return value conversion." );
break;
}
return nullptr;
}
| > | | 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
dm.emitErrorMessage( defRetVal->locationId(), "ambiguous default return value conversion." );
break;
}
return nullptr;
}
cfg->currentBB()->append( get< Value >( converted ) );
cfg->emitTerminator( r->currentLocation(), cir::Ret( r->currentLocation() ) );
}
}
verify::Func fv( c, cfg, returnType );
if( !fv.verify() )
return nullptr;
return cfg;
}
}
|
Changes to bs/diagnostics/diagnosticsmanager.h.
| ︙ | ︙ | |||
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
format( "Internal error: {}:{}: {}", __FILE__, __LINE__, message ) ); \
abort(); \
}
#define G_VAL_ASSERT( val, cond ) \
if( !( cond ) ) \
{ \
diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( (val).locationId(), \
format( "Internal error: {}:{}: assertion " #cond " failed", __FILE__, __LINE__ ) ); \
abort(); \
}
#define G_LOC_ERROR( loc, message ) \
{ \
diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( (loc), \
format( "Internal error: {}:{}: {}", __FILE__, __LINE__, message ) ); \
abort(); \
}
#define G_LOC_ASSERT( loc, cond ) \
if( !( cond ) ) \
| > > > > > > | 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
format( "Internal error: {}:{}: {}", __FILE__, __LINE__, message ) ); \
abort(); \
}
#define G_VAL_ASSERT( val, cond ) \
if( !( cond ) ) \
{ \
stringstream sstr; \
sstr << (val); \
diagnostics::DiagnosticsContext dc( (val).locationId(), format( "Value: {}", sstr.str() ) ); \
diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( (val).locationId(), \
format( "Internal error: {}:{}: assertion " #cond " failed", __FILE__, __LINE__ ) ); \
abort(); \
}
#define G_LOC_ERROR( loc, message ) \
{ \
stringstream sstr; \
sstr << (val); \
diagnostics::DiagnosticsContext dc( (val).locationId(), format( "Value: {}", sstr.str() ) ); \
diagnostics::DiagnosticsManager::GetInstance().emitErrorMessage( (loc), \
format( "Internal error: {}:{}: {}", __FILE__, __LINE__, message ) ); \
abort(); \
}
#define G_LOC_ASSERT( loc, cond ) \
if( !( cond ) ) \
|
| ︙ | ︙ |
Changes to bs/eir/hash.cpp.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include "eir.h"
#include "cir/cir.h"
namespace std
{
size_t hash< goose::eir::Hole >::operator()( const goose::eir::Hole& x ) const
{
return llvm::hash_combine( goose::util::ComputeHash( x.name() ), goose::util::ComputeHash( x.kind() ) );
}
size_t hash< goose::eir::Vector >::operator()( const goose::eir::Vector& v ) const
{
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include "eir.h"
#include "cir/cir.h"
namespace std
{
size_t hash< goose::eir::Hole >::operator()( const goose::eir::Hole& x ) const
{
return llvm::hash_combine( goose::util::ComputeHash( x.name() ), goose::util::ComputeHash( x.kind() ) );
}
size_t hash< goose::eir::Vector >::operator()( const goose::eir::Vector& v ) const
{
auto g = ContainerHashGenerator( v.terms() );
return llvm::hash_combine_range( g.begin(), g.end() );
}
size_t hash< goose::eir::Term >::operator()( const goose::eir::Term& x ) const
{
return visit( [&]< typename T >( T&& t )
{
|
| ︙ | ︙ |
Changes to bs/eir/hash.h.
1 2 3 | #ifndef GOOSE_EIR_HASH_H #define GOOSE_EIR_HASH_H | < < < < < < < < < < | 1 2 3 4 5 6 7 8 9 10 |
#ifndef GOOSE_EIR_HASH_H
#define GOOSE_EIR_HASH_H
namespace std
{
template<> struct hash< goose::eir::Hole >
{
size_t operator()( const goose::eir::Hole& x ) const;
};
|
| ︙ | ︙ |
Changes to bs/eir/value.cpp.
| ︙ | ︙ | |||
33 34 35 36 37 38 39 |
static auto poisonType = Value( TypeType(), TSID( poison ) ).setPoison();
return poisonType;
}
// A generic poisoned value of "poisontype" type.
const Value& PoisonValue()
{
| | | 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
static auto poisonType = Value( TypeType(), TSID( poison ) ).setPoison();
return poisonType;
}
// A generic poisoned value of "poisontype" type.
const Value& PoisonValue()
{
static auto poisonVal = Value( ValueToEIR( PoisonType() ), Term( 0U ) ).setPoison();
return poisonVal;
}
Term ValueToEIR( const Value& v )
{
if( v.isConstant() )
{
|
| ︙ | ︙ | |||
103 104 105 106 107 108 109 |
if( sort == "constant"_sid )
return Value( type, val, locationId );
auto cir = Decompose( val, Val< ptr< void > >() );
if( !cir )
return nullopt;
| | | 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
if( sort == "constant"_sid )
return Value( type, val, locationId );
auto cir = Decompose( val, Val< ptr< void > >() );
if( !cir )
return nullopt;
return Value( type, static_pointer_cast< cir::InstrSeq >( ptr< void >( *cir ) ), locationId );
}
Term ValueToEIR( const ValuePattern& v )
{
return VEC( TSID( value ), v.sort(), v.type(), v.val(), v.locationId() );
}
|
| ︙ | ︙ |
Changes to bs/eir/value.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#ifndef GOOSE_EIR_VALUE_H
#define GOOSE_EIR_VALUE_H
namespace goose::cir
{
class Instruction;
}
namespace goose::eir
{
class Value;
class ValuePattern;
| > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#ifndef GOOSE_EIR_VALUE_H
#define GOOSE_EIR_VALUE_H
namespace goose::cir
{
class Instruction;
using InstrSeq = list< Instruction >;
}
namespace goose::eir
{
class Value;
class ValuePattern;
|
| ︙ | ︙ | |||
30 31 32 33 34 35 36 |
m_valOrCIR( forward< VL >( valOrCIR ) ),
m_locationId( loc )
{}
const auto& type() const { return m_type; }
const auto& val() const { return get< Term >( m_valOrCIR ); }
auto& val() { return get< Term >( m_valOrCIR ); }
| | | | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
m_valOrCIR( forward< VL >( valOrCIR ) ),
m_locationId( loc )
{}
const auto& type() const { return m_type; }
const auto& val() const { return get< Term >( m_valOrCIR ); }
auto& val() { return get< Term >( m_valOrCIR ); }
ptr< cir::InstrSeq > cir() const
{
const auto* ppCir = get_if< ptr< cir::InstrSeq > >( &m_valOrCIR );
if( !ppCir )
return nullptr;
return *ppCir;
}
auto locationId() const { return m_locationId; }
auto&& setLocationId( LocationId id )
|
| ︙ | ︙ | |||
77 78 79 80 81 82 83 |
if( m_type != rhs.m_type )
return m_type < rhs.m_type;
return m_valOrCIR < rhs.m_valOrCIR;
}
private:
Term m_type;
| | | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
if( m_type != rhs.m_type )
return m_type < rhs.m_type;
return m_valOrCIR < rhs.m_valOrCIR;
}
private:
Term m_type;
variant< Term, ptr< cir::InstrSeq > > m_valOrCIR;
LocationId m_locationId = LocationId::Poison();
};
class ValuePattern
{
public:
template< typename S, typename T, typename V >
|
| ︙ | ︙ |
Changes to bs/eir/vector.h.
| ︙ | ︙ | |||
51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
int32_t weightOverride() const { return m_weightOverride; }
void setWeightOverride( int32_t wo ) { m_weightOverride = wo; }
void reserve( size_t n )
{
m_terms.reserve( n );
}
template< typename T >
void append( T&& term )
{
if constexpr( !is_same_v< Repetition, remove_cvref_t< T > > )
{
// We want the outer structures to have more
| > > > > > | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
int32_t weightOverride() const { return m_weightOverride; }
void setWeightOverride( int32_t wo ) { m_weightOverride = wo; }
void reserve( size_t n )
{
m_terms.reserve( n );
}
void resize( size_t n )
{
m_terms.resize( n );
}
template< typename T >
void append( T&& term )
{
if constexpr( !is_same_v< Repetition, remove_cvref_t< T > > )
{
// We want the outer structures to have more
|
| ︙ | ︙ | |||
72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
m_terms.emplace_back( forward< T >( term ) );
}
else
{
setRepetitionTerm( term.m_term );
}
}
template< typename... T >
static auto Make( T&&... terms )
{
auto v = make_shared< Vector >();
v->reserve( sizeof... ( T ) );
( v->append( terms ), ... );
| > > > > > > > > | 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
m_terms.emplace_back( forward< T >( term ) );
}
else
{
setRepetitionTerm( term.m_term );
}
}
template< typename T >
void set( size_t index, T&& term )
{
m_weight -= GetWeight( m_terms[index] );
m_weight += GetWeight( term ) * 2;
m_terms[index] = term;
}
template< typename... T >
static auto Make( T&&... terms )
{
auto v = make_shared< Vector >();
v->reserve( sizeof... ( T ) );
( v->append( terms ), ... );
|
| ︙ | ︙ |
Changes to bs/execute/binaryops.cpp.
1 2 3 4 5 6 7 8 | #include "execute.h" #include "builtins/builtins.h" #include "binaryops.inl" using namespace goose; using namespace goose::execute; using namespace goose::builtins; | | | | | | | | | | | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
#include "execute.h"
#include "builtins/builtins.h"
#include "binaryops.inl"
using namespace goose;
using namespace goose::execute;
using namespace goose::builtins;
bool VM::execute( const cir::And& bo )
{
return executeLogicBinOp( bo, []< typename T >( const T& lhs, const T& rhs )
{
if constexpr( is_same_v< T, bool > )
{
return lhs && rhs;
}
else
{
return lhs & rhs;
}
} );
}
bool VM::execute( const cir::Or& bo )
{
return executeLogicBinOp( bo, []< typename T >( const T& lhs, const T& rhs )
{
if constexpr( is_same_v< T, bool > )
{
return lhs || rhs;
}
else
{
return lhs | rhs;
}
} );
}
bool VM::execute( const cir::Xor& bo )
{
return executeLogicBinOp( bo, []< typename T >( const T& lhs, const T& rhs )
{
if constexpr( is_same_v< T, bool > )
{
auto l = static_cast< uint8_t >( lhs );
auto r = static_cast< uint8_t >( rhs );
return !!( l ^ r );
}
else
{
return lhs ^ rhs;
}
} );
}
bool VM::execute( const cir::Shl& bo )
{
return executeShiftBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs.shl( rhs );
} );
}
bool VM::execute( const cir::LShr& bo )
{
return executeShiftBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs.lshr( rhs );
} );
}
bool VM::execute( const cir::AShr& bo )
{
return executeShiftBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs.ashr( rhs );
} );
}
bool VM::execute( const cir::Add& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs + rhs;
} );
}
bool VM::execute( const cir::Sub& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs - rhs;
} );
}
bool VM::execute( const cir::Mul& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs * rhs;
} );
}
bool VM::execute( const cir::UDiv& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs / rhs;
} );
}
bool VM::execute( const cir::SDiv& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs / rhs;
} );
}
bool VM::execute( const cir::URem& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs % rhs;
} );
}
bool VM::execute( const cir::SRem& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs % rhs;
} );
}
bool VM::execute( const cir::Eq& bo )
{
return executeEqualityBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs == rhs;
} );
}
bool VM::execute( const cir::Neq& bo )
{
return executeEqualityBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs != rhs;
} );
}
bool VM::execute( const cir::UGT& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs > rhs;
} );
}
bool VM::execute( const cir::UGE& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs >= rhs;
} );
}
bool VM::execute( const cir::ULT& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs < rhs;
} );
}
bool VM::execute( const cir::ULE& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs <= rhs;
} );
}
bool VM::execute( const cir::SGT& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs > rhs;
} );
}
bool VM::execute( const cir::SGE& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs >= rhs;
} );
}
bool VM::execute( const cir::SLT& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs < rhs;
} );
}
bool VM::execute( const cir::SLE& bo )
{
return executeBinOp( bo, []( auto&& lhs, auto&& rhs )
{
return lhs <= rhs;
} );
}
|
Changes to bs/execute/binaryops.inl.
1 2 3 4 5 6 |
#ifndef GOOSE_EXECUTE_BINARYOPS_INL
#define GOOSE_EXECUTE_BINARYOPS_INL
namespace goose::execute
{
template< typename F >
| | > > > > | | | | | | | | | | | > | > | > | > | > | > | > | > > | | > > > > | | | | | | | | | | | > | > | > | > | > | > > | | > > > > | | < < < > > > | | | | | | | > | > | > > | | > > > > | | | | | | | | | > | > | > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
#ifndef GOOSE_EXECUTE_BINARYOPS_INL
#define GOOSE_EXECUTE_BINARYOPS_INL
namespace goose::execute
{
template< typename F >
bool VM::executeEqualityBinOp( const cir::BinaryOp& bo, F&& func )
{
auto rhs = pop();
auto lhs = pop();
if( !lhs || !rhs )
return false;
if( lhs->isPoison() || rhs->isPoison() )
return false;
assert( lhs->type() == rhs->type() );
if( lhs->type() != rhs->type() )
return false;
auto lval = Evaluate( *lhs, *this );
if( lval.isPoison() )
return false;
if( !lval.isConstant() )
return false;
auto rval = Evaluate( *rhs, *this );
if( rval.isPoison() )
return false;
if( !rval.isConstant() )
return false;
if( auto lbool = FromValue< bool >( lval ) )
{
push( ToValue( func( *lbool, *FromValue< bool >( rval ) ) ) );
return true;
}
if( auto lint = FromValue< APSInt >( lval ) )
{
auto rint = *FromValue< APSInt >( rval );
push( ToValue( func( *lint, move( rint ) ) ) );
return true;
}
if( auto lint = FromValue< BigInt >( lval ) )
{
auto rint = *FromValue< BigInt >( rval );
push( ToValue( func( *lint, move( rint ) ) ) );
return true;
}
if( auto lc = FromValue< char32_t >( lval ) )
{
auto rc = *FromValue< char32_t >( rval );
push( ToValue( func( *lc, rc ) ) );
return true;
}
if( auto lstr = FromValue< string >( lval ) )
{
const auto& rstr = *FromValue< string >( rval );
push( ToValue( func( *lstr, rstr ) ) );
return true;
}
G_TRACE_VAL( lval )
G_TRACE_VAL( rval )
G_ERROR( "binop execution failure")
return false;
}
template< typename F >
bool VM::executeLogicBinOp( const cir::BinaryOp& bo, F&& func )
{
auto rhs = pop();
auto lhs = pop();
if( !lhs || !rhs )
return false;
if( lhs->isPoison() || rhs->isPoison() )
return false;
assert( lhs->type() == rhs->type() );
if( lhs->type() != rhs->type() )
return false;
auto lval = Evaluate( *lhs, *this );
if( lval.isPoison() )
return false;
if( !lval.isConstant() )
return false;
auto rval = Evaluate( *rhs, *this );
if( rval.isPoison() )
return false;
if( !rval.isConstant() )
return false;
if( auto lbool = FromValue< bool >( lval ) )
{
push( ToValue( func( *lbool, *FromValue< bool >( rval ) ) ) );
return true;
}
if( auto lint = FromValue< APSInt >( lval ) )
{
auto rint = *FromValue< APSInt >( rval );
push( ToValue( func( *lint, move( rint ) ) ) );
return true;
}
if( auto lint = FromValue< BigInt >( lval ) )
{
auto rint = *FromValue< BigInt >( rval );
push( ToValue( func( *lint, move( rint ) ) ) );
return true;
}
G_TRACE_VAL( lval )
G_TRACE_VAL( rval )
G_ERROR( "binop execution failure")
return false;
}
template< typename F >
bool VM::executeBinOp( const cir::BinaryOp& bo, F&& func )
{
auto rhs = pop();
auto lhs = pop();
if( !lhs || !rhs )
return false;
if( lhs->isPoison() || rhs->isPoison() )
return false;
assert( lhs->type() == rhs->type() );
if( lhs->type() != rhs->type() )
return false;
auto lval = Evaluate( *lhs, *this );
if( lval.isPoison() )
return false;
if( !lval.isConstant() )
return false;
auto rval = Evaluate( *rhs, *this );
if( rval.isPoison() )
return false;
if( !rval.isConstant() )
return false;
if( auto lint = FromValue< APSInt >( lval ) )
{
auto rint = *FromValue< APSInt >( rval );
push( ToValue( func( *lint, move( rint ) ) ) );
return true;
}
if( auto lint = FromValue< BigInt >( lval ) )
{
const auto& rint = *FromValue< BigInt >( rval );
push( ToValue( func( *lint, rint ) ) );
return true;
}
G_TRACE_VAL( lval )
G_TRACE_VAL( rval )
G_ERROR( "binop execution failure")
return false;
}
template< typename F >
bool VM::executeShiftBinOp( const cir::BinaryOp& bo, F&& func )
{
auto rhs = pop();
auto lhs = pop();
if( !lhs || !rhs )
return false;
if( lhs->isPoison() || rhs->isPoison() )
return false;
auto lval = Evaluate( *lhs, *this );
if( lval.isPoison() )
return false;
if( !lval.isConstant() )
return false;
auto rval = Evaluate( *rhs, *this );
if( rval.isPoison() )
return false;
if( !rval.isConstant() )
return false;
if( auto lint = FromValue< APSInt >( lval ) )
{
auto rint = *FromValue< APSInt >( rval );
push( ToValue( APSInt( func( *lint, move( rint ) ), lint->isUnsigned() ) ) );
return true;
}
if( auto lint = FromValue< BigInt >( lval ) )
{
auto rint = *FromValue< APSInt >( rval );
push( ToValue( BigInt( func( *lint, move( rint ) ) ) ) );
return true;
}
G_TRACE_VAL( lval )
G_TRACE_VAL( rval )
G_ERROR( "binop execution failure")
return false;
}
}
#endif
|
Changes to bs/execute/eval.cpp.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 |
if( !v.isConstant() )
{
const auto& cir = val.cir();
if( !cir )
return v.setPoison();
| < < > > > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
if( !v.isConstant() )
{
const auto& cir = val.cir();
if( !cir )
return v.setPoison();
// Execution may fail: there are some cases when we can't really
// be sure that eager evaluation is possible until we actually try.
// In this case we forget about the eager evaluation and return the
// value as is.
if( !vm.execute( *cir ) )
return v;
// Not returning a value isn't a failure, for instance
// if the value we just evaluated was a call to a function returning void.
// So we return a void typed value in this case, otherwise the original
// value might get evaluated again. And since void function are usually
// called for their side effects, we don't want to evaluate them twice.
auto result = vm.pop();
if( !result )
return v;
if( result->isPoison() )
return *result;
v = move( *result );
|
| ︙ | ︙ |
Changes to bs/execute/vm.cpp.
1 2 3 4 5 6 7 8 9 10 11 |
#include "execute.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::execute;
using namespace goose::builtins;
uint32_t VM::ms_remainingBranchInstExecutions = 1 << 24;
optional< Value > VM::execute( CFG& cfg )
{
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > | > | > > > > | > > > > > > > > > > > | | | > > > > | | | | < < < | | | < < > > | > | | > | < < < | | < | < < > | > | < < | | > | | | > > | | | | > | < | < | | | | | > > > > | > > > | > | > > | | > > | > > > > > > | | | | > | > > > > < < | | | > | | | | | | | | > | > > > > > > > > | > > > > | | | | | > | | | | | | | | > > | > > | > | > > > > > > > > | | | | < < < < | | > > > | | > | | | | | | | > > > > > > > | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 |
#include "execute.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::execute;
using namespace goose::builtins;
uint32_t VM::ms_remainingBranchInstExecutions = 1 << 24;
VM::StackFrameHelper::StackFrameHelper( VM& vmIn, CFG& cfg, bool shouldActivate ) :
vm( vmIn )
{
savedStackSize = vm.m_stack.size();
savedFrameStart = vm.m_currentFrameStart;
// Reserve stack space for the temporaries + some more for local scratch space
// The stack will be extended as necessary if that's not enough
// (TODO define that as a constant somewhere)
vm.m_stack.reserve( vm.m_stack.size() + cfg.temporariesCount() + 16 );
vm.m_stack.resize( vm.m_stack.size() + cfg.temporariesCount() );
if( shouldActivate )
activate();
}
void VM::StackFrameHelper::activate()
{
vm.m_currentFrameStart = savedStackSize;
}
VM::StackFrameHelper::~StackFrameHelper()
{
vm.m_stack.resize( savedStackSize );
vm.m_currentFrameStart = savedFrameStart;
}
optional< Value > VM::execute( CFG& cfg )
{
StackFrameHelper sfh( *this, cfg );
if( !execute( cfg.entryBB() ) )
return nullopt;
return pop();
}
bool VM::execute( ptr< BasicBlock > bb )
{
m_retVal = nullopt;
auto pbbBackup = m_pPreviousBB;
while( bb )
{
const auto* pInstrs = bb->runnableInstructions();
if( !pInstrs )
return false;
for( auto&& instr : *pInstrs )
{
if( !execute( instr ) )
return false;
}
if( !bb->terminator() )
break;
m_pPreviousBB = bb;
bb = executeTerminator( *bb->terminator() );
}
m_pPreviousBB = pbbBackup;
if( m_retVal )
push( *m_retVal );
return true;
}
bool VM::execute( const cir::InstrSeq& is )
{
for( const auto& instr : is )
{
if( !execute( instr ) )
return false;
}
return true;
}
bool VM::execute( const cir::Instruction& instr )
{
return visit( [&]( auto&& e )
{
return execute( e );
}, instr.content() );
}
bool VM::execute( const cir::Call& call )
{
if( !( ms_remainingBranchInstExecutions ) )
{
DiagnosticsManager::GetInstance().emitErrorMessage( 0,
"execute: compilation time execution budget exceeded." );
return false;
}
--ms_remainingBranchInstExecutions;
auto callFunc = pop();
if( !callFunc )
return false;
if( callFunc->isPoison() )
DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );
auto func = Evaluate( *callFunc, *this );
if( func.isPoison() )
return false;
if( !func.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage( 0,
"execute: function evaluation failed." );
return false;
}
if( IsExternalFunc( func ) )
return false;
auto argCount = call.numArgs();
if( IsBuiltinFunc( func ) )
{
auto newVec = make_shared< Vector >();
newVec->resize( argCount );
for( uint32_t argIndex = 0; argIndex < argCount; ++argIndex )
{
auto val = pop();
if( !val )
return false;
auto newVal = Evaluate( *val, *this );
if( newVal.isPoison() )
return false;
if( !newVal.isConstant() )
return false;
newVec->set( argCount - argIndex - 1, ValueToEIR( newVal ) );
}
auto result = ExecuteBuiltinFuncCall( func, TERM( newVec ) );
if( result )
push( *result );
return true;
}
const auto* pFunc = GetFuncCIR( func );
if( !pFunc || !pFunc->isValid() )
return false;
// Pop and eval all args before creating our stackframe
llvm::SmallVector< Term, 8 > evaluatedArgs( argCount, {} );
for( uint32_t argIndex = 0; argIndex < argCount; ++argIndex )
{
auto val = pop();
if( !val )
return false;
auto newVal = Evaluate( *val, *this );
if( newVal.isPoison() )
return false;
if( !newVal.isConstant() )
return false;
evaluatedArgs[argCount - argIndex - 1] = ValueToEIR( newVal );
}
optional< Value > result;
{
StackFrameHelper sfh( *this, *pFunc->body(), false );
uint32_t argIndex = 0;
for( auto&& a : evaluatedArgs )
sfh.set( argIndex++, make_shared< Term >( move( a ) ) );
sfh.activate();
// Directly execute the entry BB rather than the CFG: we already have setup the stack frame
// execute( CFG ) is intended for situations where we need to execute a CFG directly without
// it being part of a function
if( !execute( pFunc->body()->entryBB() ) )
return false;
if( *GetFuncRType( func ) != GetValueType< void >() )
result = pop();
}
if( result )
push( *result );
return true;
}
optional< Value > VM::ExecuteBuiltinFuncCall( const Value& func, const Term& args )
{
const auto& f = GetBuiltinFuncWrapper( func );
return f( args );
}
bool VM::execute( const cir::Constant& cst )
{
push( cst.value() );
return true;
}
bool VM::execute( const cir::VarAddr& va )
{
auto stackIndex = m_currentFrameStart + va.varIndex();
if( stackIndex >= m_stack.size() )
return false;
if( !m_stack[stackIndex] )
return false;
push( ToValue( m_stack[stackIndex].get() ) );
return true;
}
bool VM::execute( const cir::TempAddr& ta )
{
auto initVal = pop();
if( !initVal )
return false;
auto stackIndex = m_currentFrameStart + ta.tempIndex();
if( stackIndex >= m_stack.size() )
return false;
if( !m_stack[stackIndex] )
m_stack[stackIndex] = make_shared< Term >( ValueToEIR( Evaluate( *initVal, *this ) ) );
push( ToValue( m_stack[stackIndex].get() ) );
return true;
}
bool VM::execute( const cir::Select& s )
{
auto baseAddr = pop();
if( !baseAddr )
return false;
auto pvoid = FromValue< Term* >( *baseAddr );
if( !pvoid )
return false;
auto val = EIRToValue( *static_cast< Term* >( pvoid ) );
if( !val )
return false;
// We only support tuples now. In the future, Select will also be used to
// add an offset to another pointer.
// Everything else (structs, classes, containers, etc.) should build on top
// of those two fundamental types.
if( !IsTuple( *val ) )
{
G_VAL_ERROR( *val, "execute: select: value is not a tuple" );
return false;
}
assert( val->isConstant() );
push( ToValue( &GetTupleElement( *val, s.memberIndex() ) ) );
return true;
}
// TODO do we still need to handle this here now that the CFG tells us in advance how many slots to reserve?
// (we could return the uninitialized value in Load if the slot ptr is null)
// Initialization could be explicit with a store, like wtf did I do that
// only used by shortcut eval operators btw
// -> oh yeah I remember its because I don't want to actually emit stores for these in llvm. fuck
// ok maybe leave this one be but it doesn't need to extend the stack, it can just check if its big enough
// and fuck off if it isn't, just so that invalid CIR code emited by user extensions doesn't crash
bool VM::execute( const cir::CreateTemporary& ct )
{
auto val = pop();
if( !val )
return false;
auto stackIndex = m_currentFrameStart + ct.index();
if( m_stack.size() <= stackIndex )
m_stack.resize( stackIndex + 1 );
m_stack[stackIndex] = make_shared< Term >( ValueToEIR( Evaluate( *val, *this ) ) );
return true;
}
bool VM::execute( const cir::GetTemporary& gt )
{
auto stackIndex = gt.index() + m_currentFrameStart;
if( stackIndex >= m_stack.size() )
return false;
push( *EIRToValue( *m_stack[stackIndex] ) );
return true;
}
bool VM::execute( const cir::AllocVar& av )
{
auto stackIndex = m_currentFrameStart + av.index();
if( m_stack.size() <= stackIndex )
m_stack.resize( stackIndex + 1 );
m_stack[stackIndex] = BuildUninitializedValue( av.type() );
return true;
}
bool VM::execute( const cir::Load& l )
{
auto baseAddr = pop();
if( !baseAddr )
return false;
auto addr = FromValue< Term* >( *baseAddr );
if( !addr )
return false;
auto val = EIRToValue( *addr );
if( !val )
return false;
push( *EIRToValue( *addr ) );
return true;
}
bool VM::execute( const cir::Store& s )
{
auto val = pop();
if( !val )
return false;
auto result = Evaluate( *val, *this );
if( !result.isConstant() )
return false;
auto baseAddr = pop();
if( !baseAddr )
return false;
auto addr = FromValue< Term* >( *baseAddr );
if( !addr )
return false;
*addr = ValueToEIR( result );
return true;
}
bool VM::execute( const cir::Phi& p )
{
auto stackIndex = m_currentFrameStart + p.destIndex();
if( m_stack.size() <= stackIndex )
m_stack.resize( stackIndex + 1 );
bool success = false;
p.forAllIncomings( [&]( auto&& bb, auto&& val )
{
if( bb == m_pPreviousBB )
{
m_stack[stackIndex] = make_shared< Term >( ValueToEIR( Evaluate( val, *this ) ) );
success = true;
return false;
}
return true;
} );
return success;
}
bool VM::execute( const cir::Not& uo )
{
auto operand = pop();
if( !operand || operand->isPoison() )
return false;
auto opval = Evaluate( *operand, *this );
if( opval.isPoison() )
return false;
if( !opval.isConstant() )
return false;
auto boolVal = FromValue< bool >( opval );
if( !boolVal )
return false;
push( ToValue( !*boolVal ) );
return true;
}
ptr< BasicBlock > VM::executeTerminator( const cir::Terminator& terminator )
{
return visit( [&]( auto&& e )
{
return executeTerminator( e );
}, terminator.content() );
}
ptr< BasicBlock > VM::executeTerminator( const cir::RetVoid& r )
{
return nullptr;
}
ptr< BasicBlock > VM::executeTerminator( const cir::Ret& r )
{
auto retVal = pop();
if( !retVal )
return nullptr;
m_retVal = Evaluate( *retVal, *this );
return nullptr;
}
ptr< BasicBlock > VM::executeTerminator( const cir::Branch& b )
{
if( !( ms_remainingBranchInstExecutions ) )
{
|
| ︙ | ︙ | |||
339 340 341 342 343 344 345 |
"execute: compilation time execution budget exceeded." );
m_retVal = PoisonValue();
return nullptr;
}
--ms_remainingBranchInstExecutions;
| > > > > | | | 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 |
"execute: compilation time execution budget exceeded." );
m_retVal = PoisonValue();
return nullptr;
}
--ms_remainingBranchInstExecutions;
auto condVal = pop();
if( !condVal )
return nullptr;
auto cond = Evaluate( *condVal, *this );
if( cond.isPoison() )
return nullptr;
if( !cond.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage( condVal->locationId(),
"execute: branch condition evaluation failed." );
m_retVal = PoisonValue();
return nullptr;
}
if( *FromValue< bool >( cond ) )
return cb.trueDest().lock();
|
| ︙ | ︙ |
Changes to bs/execute/vm.h.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 |
public:
static void SetExecutionBudget( uint32_t b )
{
ms_remainingBranchInstExecutions = b;
}
optional< Value > execute( CFG& cfg );
| > | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
public:
static void SetExecutionBudget( uint32_t b )
{
ms_remainingBranchInstExecutions = b;
}
optional< Value > execute( CFG& cfg );
bool execute( ptr< BasicBlock > bb );
bool execute( const cir::InstrSeq& is );
bool execute( const cir::Instruction& instr );
bool execute( const cir::Call& call );
bool execute( const cir::Constant& cst );
bool execute( const cir::VarAddr& va );
bool execute( const cir::TempAddr& ta );
bool execute( const cir::Select& s );
bool execute( const cir::CreateTemporary& ct );
bool execute( const cir::GetTemporary& gt );
bool execute( const cir::AllocVar& av );
bool execute( const cir::Load& l );
bool execute( const cir::Store& s );
bool execute( const cir::Phi& p );
bool execute( const cir::Not& uo );
bool execute( const cir::And& bo );
bool execute( const cir::Or& bo );
bool execute( const cir::Xor& bo );
bool execute( const cir::Shl& bo );
bool execute( const cir::LShr& bo );
bool execute( const cir::AShr& bo );
bool execute( const cir::Add& bo );
bool execute( const cir::Sub& bo );
bool execute( const cir::Mul& bo );
bool execute( const cir::UDiv& bo );
bool execute( const cir::SDiv& bo );
bool execute( const cir::URem& bo );
bool execute( const cir::SRem& bo );
bool execute( const cir::Eq& bo );
bool execute( const cir::Neq& bo );
bool execute( const cir::UGT& bo );
bool execute( const cir::UGE& bo );
bool execute( const cir::ULT& bo );
bool execute( const cir::ULE& bo );
bool execute( const cir::SGT& bo );
bool execute( const cir::SGE& bo );
bool execute( const cir::SLT& bo );
bool execute( const cir::SLE& bo );
template< typename T >
bool execute( const T& )
{
return false;
}
ptr< BasicBlock > executeTerminator( const cir::Terminator& terminator );
ptr< BasicBlock > executeTerminator( const cir::RetVoid& r );
ptr< BasicBlock > executeTerminator( const cir::Ret& r );
ptr< BasicBlock > executeTerminator( const cir::Branch& b );
ptr< BasicBlock > executeTerminator( const cir::CondBranch& cb );
ptr< BasicBlock > executeTerminator( const cir::GhostBranch& gb );
template< typename T >
ptr< BasicBlock > executeTerminator( const T& )
{
return nullptr;
}
void push( const Value& v )
{
m_stack.emplace_back( make_shared< Term >( ValueToEIR( v ) ) );
}
optional< Value > pop()
{
if( m_stack.empty() || m_stack.size() <= m_currentFrameStart )
return nullopt;
assert( m_stack.back() );
auto result = move( *m_stack.back() );
m_stack.resize( m_stack.size() - 1 );
return EIRToValue( result );
}
private:
struct StackFrameHelper
{
StackFrameHelper( VM& vm, CFG& cfg, bool shouldActivate = true );
~StackFrameHelper();
void activate();
template< typename T >
void set( size_t index, T&& val )
{
vm.m_stack[index + savedStackSize] = forward< T >( val );
}
size_t savedStackSize = 0;
size_t savedFrameStart = 0;
VM& vm;
};
static optional< Value > ExecuteBuiltinFuncCall( const Value& func, const Term& args );
static ptr< Term > BuildUninitializedValue( const Value& type );
template< typename F >
bool executeEqualityBinOp( const cir::BinaryOp& bo, F&& func );
template< typename F >
bool executeLogicBinOp( const cir::BinaryOp& bo, F&& func );
template< typename F >
bool executeBinOp( const cir::BinaryOp& bo, F&& func );
template< typename F >
bool executeShiftBinOp( const cir::BinaryOp& bo, F&& func );
llvm::SmallVector< ptr< Term >, 8 > m_stack;
optional< Value > m_retVal;
size_t m_currentFrameStart = 0;
// Used to interpret Phi instructions.
ptr< BasicBlock > m_pPreviousBB;
|
| ︙ | ︙ |
Changes to bs/g0api/compiler.cpp.
| ︙ | ︙ | |||
49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
DiagnosticsManager::GetInstance().emitErrorMessage( enable.locationId(),
"this doesn't evaluate to a constant." );
return;
}
verify::Func::SetDumpSolverOnSuccess( *FromValue< bool >( enable ) );
} );
RegisterBuiltinFunc< Intrinsic< void ( uint32_t ) > >( e, "#SetExecutionBudget"_sid,
[]( auto&& c, const Value& budget )
{
static bool used = false;
if( !budget.isConstant() )
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
DiagnosticsManager::GetInstance().emitErrorMessage( enable.locationId(),
"this doesn't evaluate to a constant." );
return;
}
verify::Func::SetDumpSolverOnSuccess( *FromValue< bool >( enable ) );
} );
RegisterBuiltinFunc< Intrinsic< void ( CustomPattern< Value, FuncPattern >, string ) > >( e, "#DumpFunctionCFG"_sid,
[]( auto&& c, const Value& funcVal, const Value& filename )
{
if( !funcVal.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage( funcVal.locationId(),
"this doesn't evaluate to a constant." );
return;
}
if( !filename.isConstant() )
{
DiagnosticsManager::GetInstance().emitErrorMessage( filename.locationId(),
"this doesn't evaluate to a constant." );
return;
}
auto name = *FromValue< string >( filename );
auto func = CompileFunc( c, funcVal );
if( func.isPoison() || DiagnosticsManager::GetInstance().errorsWereEmitted() )
return;
auto f = *FromValue< builtins::Func >( func );
if( !f.cir() )
return;
CfgViz( name.c_str(), *f.cir() );
} );
RegisterBuiltinFunc< Intrinsic< void ( uint32_t ) > >( e, "#SetExecutionBudget"_sid,
[]( auto&& c, const Value& budget )
{
static bool used = false;
if( !budget.isConstant() )
|
| ︙ | ︙ |
Changes to bs/g0api/extensibility/cir.cpp.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
namespace
{
template< typename C, typename T, typename O, O opCode, typename... Params >
void RegisterMkOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
{
RegisterBuiltinFunc< TypeWrapper< ptr< C > >
( ConstantParam< uint8_t, static_cast< uint8_t >( opCode ) >, Params... ) >( e, pOvlSet,
| | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
namespace
{
template< typename C, typename T, typename O, O opCode, typename... Params >
void RegisterMkOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
{
RegisterBuiltinFunc< TypeWrapper< ptr< C > >
( ConstantParam< uint8_t, static_cast< uint8_t >( opCode ) >, Params... ) >( e, pOvlSet,
[]( uint8_t, const auto&... params ) -> TypeWrapper< ptr< C > >
{
return make_shared< C >( T( WrappedValueAccessor< Params >::Get( params )... ) );
} );
}
template< typename C, typename T, typename O, O opCode, typename... Getters >
void RegisterUnpackOverload( Env& e, const ptr< OverloadSet >& pOvlSet, Getters... getters )
{
using tup_type = tuple< WrapType<
remove_cvref_t< decltype( ( static_cast< T* >( nullptr )->*getters )() ) > >... >;
RegisterBuiltinFunc< bool ( ConstantParam< uint8_t, static_cast< uint8_t >( opCode ) >,
TypeWrapper< ptr< C > >, TermRef< tup_type > ) >( e, pOvlSet,
[&]( uint8_t, const auto& instr, auto& out )
{
const T* pt = get_if< T >( &instr->content() );
if( !pt )
return false;
out = tup_type( ( pt->*getters )()... );
|
| ︙ | ︙ | |||
84 85 86 87 88 89 90 |
SGT,
SGE,
SLT,
SLE,
Assert,
Placeholder,
| | > | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
SGT,
SGE,
SLT,
SLE,
Assert,
Placeholder,
PHOverrideSet,
PHOverrideClear
};
template< typename T, InstrOpCode opCode, typename... Params >
void RegisterMkInstrOverload( Env& e, const ptr< OverloadSet >& pOvlSet )
{
RegisterMkOverload< Instruction, T, InstrOpCode, opCode, Params... >( e, pOvlSet );
}
|
| ︙ | ︙ | |||
111 112 113 114 115 116 117 |
remove_cvref_t< decltype( ( static_cast< T* >( nullptr )->*getters )() ) > >... >( e, MkInstr );
RegisterUnpackInstrOverload< T, opCode >( e, UnpackInstr );
}
template< typename T, InstrOpCode opCode >
void RegisterBinOpInstrOverloads( Env& e, const ptr< OverloadSet >& MkInstr, const ptr< OverloadSet >& UnpackInstr )
{
| | > | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
remove_cvref_t< decltype( ( static_cast< T* >( nullptr )->*getters )() ) > >... >( e, MkInstr );
RegisterUnpackInstrOverload< T, opCode >( e, UnpackInstr );
}
template< typename T, InstrOpCode opCode >
void RegisterBinOpInstrOverloads( Env& e, const ptr< OverloadSet >& MkInstr, const ptr< OverloadSet >& UnpackInstr )
{
RegisterInstrOverloads< T, opCode >( e, MkInstr, UnpackInstr, &T::locationId );
}
// This enum must match the order of the types in the Terminator variant.
enum class TermiOpCode
{
RetVoid,
Ret,
Branch,
CondBranch,
Break,
Continue
};
|
| ︙ | ︙ | |||
191 192 193 194 195 196 197 |
DefineConstant( e, "InstrOpCodeULE"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::ULE ) ) ) );
DefineConstant( e, "InstrOpCodeSGT"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SGT ) ) ) );
DefineConstant( e, "InstrOpCodeSGE"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SGE ) ) ) );
DefineConstant( e, "InstrOpCodeSLT"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SLT ) ) ) );
DefineConstant( e, "InstrOpCodeSLE"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SLE ) ) ) );
DefineConstant( e, "InstrOpCodeAssert"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Assert ) ) ) );
DefineConstant( e, "InstrOpCodePlaceholder"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Placeholder ) ) ) );
| | > | | | | | | | | | | | | | 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
DefineConstant( e, "InstrOpCodeULE"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::ULE ) ) ) );
DefineConstant( e, "InstrOpCodeSGT"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SGT ) ) ) );
DefineConstant( e, "InstrOpCodeSGE"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SGE ) ) ) );
DefineConstant( e, "InstrOpCodeSLT"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SLT ) ) ) );
DefineConstant( e, "InstrOpCodeSLE"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::SLE ) ) ) );
DefineConstant( e, "InstrOpCodeAssert"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Assert ) ) ) );
DefineConstant( e, "InstrOpCodePlaceholder"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::Placeholder ) ) ) );
DefineConstant( e, "InstrOpCodePHOverrideSet"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::PHOverrideSet ) ) ) );
DefineConstant( e, "InstrOpCodePHOverrideClear"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( InstrOpCode::PHOverrideClear ) ) ) );
RegisterBuiltinFunc< uint8_t ( TypeWrapper< ptr< Instruction > > ) >( e, "GetInstrOpCode"_sid,
[]( const auto& t )
{
return t->content().index();
} );
// MkInstr and UnpackInstr overloads
auto MkInstr = CreateOverloadSet( e, "MkInstr"_sid );
auto UnpackInstr = CreateOverloadSet( e, "UnpackInstr"_sid );
RegisterInstrOverloads< Call, InstrOpCode::Call >( e, MkInstr, UnpackInstr, &Call::numArgs, &Call::locationId );
RegisterInstrOverloads< VarAddr, InstrOpCode::VarAddr >( e, MkInstr, UnpackInstr, &VarAddr::varIndex, &VarAddr::type, &VarAddr::locationId );
RegisterInstrOverloads< TempAddr, InstrOpCode::TempAddr >( e, MkInstr, UnpackInstr, &TempAddr::tempIndex, &TempAddr::locationId );
RegisterInstrOverloads< Select, InstrOpCode::Select >( e, MkInstr, UnpackInstr, &Select::memberIndex, &Select::locationId );
RegisterInstrOverloads< CreateTemporary, InstrOpCode::CreateTemporary >( e, MkInstr, UnpackInstr, &CreateTemporary::index, &CreateTemporary::locationId );
RegisterInstrOverloads< GetTemporary, InstrOpCode::GetTemporary >( e, MkInstr, UnpackInstr, &GetTemporary::type, &GetTemporary::index, &GetTemporary::locationId );
RegisterInstrOverloads< AllocVar, InstrOpCode::AllocVar >( e, MkInstr, UnpackInstr, &AllocVar::type, &AllocVar::index, &AllocVar::locationId );
RegisterInstrOverloads< Load, InstrOpCode::Load >( e, MkInstr, UnpackInstr, &Load::type, &Load::locationId );
RegisterInstrOverloads< Store, InstrOpCode::Store >( e, MkInstr, UnpackInstr, &Store::type, &Store::srcLocId, &Store::destLocId );
RegisterInstrOverloads< Phi, InstrOpCode::Phi >( e, MkInstr, UnpackInstr, &Phi::type, &Phi::numIncomings, &Phi::destIndex, &Phi::locationId );
RegisterInstrOverloads< Not, InstrOpCode::Not >( e, MkInstr, UnpackInstr, &Not::locationId );
RegisterBinOpInstrOverloads< And, InstrOpCode::And >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< Or, InstrOpCode::Or >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< Xor, InstrOpCode::Xor >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< Implies, InstrOpCode::Implies >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< Shl, InstrOpCode::Shl >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< LShr, InstrOpCode::LShr >( e, MkInstr, UnpackInstr );
|
| ︙ | ︙ | |||
240 241 242 243 244 245 246 |
RegisterBinOpInstrOverloads< ULT, InstrOpCode::ULT >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< ULE, InstrOpCode::ULE >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< SGT, InstrOpCode::SGT >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< SGE, InstrOpCode::SGE >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< SLT, InstrOpCode::SLT >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< SLE, InstrOpCode::SLE >( e, MkInstr, UnpackInstr );
| | | | > > < < < < < < < < < < | < < < | | | | | | | | < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 |
RegisterBinOpInstrOverloads< ULT, InstrOpCode::ULT >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< ULE, InstrOpCode::ULE >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< SGT, InstrOpCode::SGT >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< SGE, InstrOpCode::SGE >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< SLT, InstrOpCode::SLT >( e, MkInstr, UnpackInstr );
RegisterBinOpInstrOverloads< SLE, InstrOpCode::SLE >( e, MkInstr, UnpackInstr );
RegisterInstrOverloads< Assert, InstrOpCode::Assert >( e, MkInstr, UnpackInstr, &Assert::locationId );
RegisterInstrOverloads< Placeholder, InstrOpCode::Placeholder >( e, MkInstr, UnpackInstr, &Placeholder::type, &Placeholder::name, &Placeholder::locationId );
RegisterInstrOverloads< PHOverrideSet, InstrOpCode::PHOverrideSet >( e, MkInstr, UnpackInstr, &PHOverrideSet::name, &PHOverrideSet::locationId );
RegisterInstrOverloads< PHOverrideClear, InstrOpCode::PHOverrideClear >( e, MkInstr, UnpackInstr, &PHOverrideClear::name, &PHOverrideClear::locationId );
// TODO functions to manipulate Phi's incomings array
////////////////////////////
// Terminator
////////////////////////////
DefineConstant( e, "TermiOpCodeRetVoid"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::RetVoid ) ) ) );
DefineConstant( e, "TermiOpCodeRet"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Ret ) ) ) );
DefineConstant( e, "TermiOpCodeBranch"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Branch ) ) ) );
DefineConstant( e, "TermiOpCodeCondBranch"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::CondBranch ) ) ) );
DefineConstant( e, "TermiOpCodeBreak"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Break ) ) ) );
DefineConstant( e, "TermiOpCodeContinue"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermiOpCode::Continue ) ) ) );
// MkTermi and UnpackTermi overloads
auto MkTermi = CreateOverloadSet( e, "MkTermi"_sid );
auto UnpackTermi = CreateOverloadSet( e, "UnpackTermi"_sid );
RegisterTermiOverloads< RetVoid, TermiOpCode::RetVoid >( e, MkTermi, UnpackTermi, &RetVoid::locationId );
RegisterTermiOverloads< Ret, TermiOpCode::Ret >( e, MkTermi, UnpackTermi, &Ret::locationId );
RegisterTermiOverloads< Branch, TermiOpCode::Branch >( e, MkTermi, UnpackTermi, &Branch::dest );
RegisterTermiOverloads< CondBranch, TermiOpCode::CondBranch >( e, MkTermi, UnpackTermi, &CondBranch::trueDest, &CondBranch::falseDest );
RegisterTermiOverloads< Break, TermiOpCode::Break >( e, MkTermi, UnpackTermi, &Break::level );
RegisterTermiOverloads< Continue, TermiOpCode::Continue >( e, MkTermi, UnpackTermi, &Continue::level );
////////////////////////////
// BasicBlock
////////////////////////////
RegisterBuiltinFunc< TypeWrapper< LocationId > ( TypeWrapper< ptr< BasicBlock > > ) >( e, "GetBasicBlockLocation"_sid,
[]( const auto& pBB ) -> TypeWrapper< LocationId >
{
return pBB->locationId();
} );
RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< LocationId > ) >( e, "SetBasicBlockLocation"_sid,
[]( const auto& pBB, const auto& loc )
{
pBB->setLocationId( loc );
} );
RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< BasicBlock > > ) >( e, "GetBasicBlockLoopId"_sid,
[]( const auto& pBB )
{
return pBB->loopId();
} );
RegisterBuiltinFunc< bool ( TypeWrapper< ptr< BasicBlock > > ) >( e, "IsBasicBlockLoopHeader"_sid,
[]( const auto& pBB )
{
return pBB->isLoopHeader();
} );
RegisterBuiltinFunc< bool ( TypeWrapper< ptr< BasicBlock > >, TermRef< TypeWrapper< ptr< Terminator > > > ) >
( e, "GetBasicBlockTerminator"_sid,
[]( const auto& pBB, auto& out )
{
const auto& bb = *pBB.get();
if( !bb.terminator() )
return false;
out = make_shared< Terminator >( *bb.terminator() );
return true;
} );
RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< ptr< Terminator > > ) >( e, "SetBasicBlockTerminator"_sid,
[]( const auto& pBB, const auto& termi )
{
pBB->setTerminator( *termi.get() );
} );
// TODO InstrSeq, which will be annoying to deal with (we can't just iterate by indexing it so we'll need to figure something out for the api)
RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< ptr< Instruction > > ) >
( e, "AppendBasicBlockInstr"_sid,
[]( const auto& pBB, auto& instr )
{
auto& bb = *pBB.get();
bb.append( *instr.get() );
} );
RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< ptr< InstrSeq > > ) >
( e, "AppendBasicBlockInstr"_sid,
[]( const auto& pBB, auto& is )
{
auto& bb = *pBB.get();
bb.append( *is.get() );
} );
RegisterBuiltinFunc< void ( TypeWrapper< ptr< BasicBlock > >, TypeWrapper< Value > ) >
( e, "AppendBasicBlockInstr"_sid,
[]( const auto& pBB, auto& val )
{
auto& bb = *pBB.get();
bb.append( val.get() );
} );
////////////////////////////
// CFG
////////////////////////////
RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > > ) >( e, "IsCFGPoisoned"_sid,
[]( const auto& pCFG )
{
return pCFG->isPoisoned();
} );
RegisterBuiltinFunc< void ( TypeWrapper< ptr< CFG > > ) >( e, "PoisonCFG"_sid,
[]( const auto& pCFG )
{
pCFG->poison();
} );
RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< CFG > > ) >( e, "GetCFGBasicBlockCount"_sid,
[]( const auto& pCFG )
{
return static_cast< uint32_t >( pCFG->count() );
} );
RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > >, uint32_t, TermRef< TypeWrapper< ptr< BasicBlock > > > ) >
( e, "GetCFGBasicBlock"_sid,
[]( const auto& pCFG, uint32_t index, auto& out )
{
const auto& cfg = *pCFG.get();
if( cfg.count() <= index )
return false;
out = cfg.getBB( index );
return true;
} );
RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > >, TermRef< TypeWrapper< ptr< BasicBlock > > > ) >
( e, "GetCFGEntryBasicBlock"_sid,
[]( const auto& pCFG, auto& out )
{
const auto& cfg = *pCFG.get();
if( cfg.count() == 0 )
return false;
out = cfg.entryBB();
return true;
} );
RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > >, TermRef< TypeWrapper< ptr< BasicBlock > > > ) >
( e, "GetCFGLastBasicBlock"_sid,
[]( const auto& pCFG, auto& out )
{
const auto& cfg = *pCFG.get();
if( cfg.count() == 0 )
return false;
out = cfg.lastBB();
return true;
} );
RegisterBuiltinFunc< bool ( TypeWrapper< ptr< CFG > >, TermRef< TypeWrapper< ptr< BasicBlock > > > ) >
( e, "GetCFGCurrentBasicBlock"_sid,
[]( const auto& pCFG, auto& out )
{
const auto& cfg = *pCFG.get();
if( !cfg.currentBB() )
return false;
out = cfg.currentBB();
return true;
} );
RegisterBuiltinFunc< void ( TypeWrapper< ptr< CFG > >, TypeWrapper< ptr< BasicBlock > > ) >
( e, "SetCFGCurrentBasicBlock"_sid,
[]( const auto& pCFG, auto& pBB )
{
auto& cfg = *pCFG.get();
cfg.setCurrentBB( pBB.get() );
} );
RegisterBuiltinFunc< TypeWrapper< ptr< BasicBlock > > ( TypeWrapper< ptr< CFG > > ) >
( e, "CFGCreateBasicBlock"_sid,
[]( const auto& pCFG ) -> TypeWrapper< ptr< BasicBlock > >
{
auto& cfg = *pCFG.get();
return cfg.createBB();
} );
RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< CFG > > ) >
( e, "CFGGetNewTemporaryIndex"_sid,
[]( const auto& pCFG )
{
auto& cfg = *pCFG.get();
return cfg.getNewTemporaryIndex();
} );
////////////////////////////
// Decorator
////////////////////////////
RegisterBuiltinFunc< void ( TypeWrapper< ptr< Decorator > >, TypeWrapper< ptr< Instruction > > ) >
( e, "DecoratorAddPrologueInstr"_sid,
[]( const auto& pBB, auto& instr )
{
auto& bb = *pBB.get();
bb.addPrologueInstr( *instr.get() );
} );
RegisterBuiltinFunc< void ( TypeWrapper< ptr< Decorator > >, TypeWrapper< ptr< Instruction > > ) >
( e, "DecoratorAddOperationInstr"_sid,
[]( const auto& pBB, auto& instr )
{
auto& bb = *pBB.get();
bb.addOperationInstr( *instr.get() );
} );
RegisterBuiltinFunc< void ( TypeWrapper< ptr< Decorator > >, TypeWrapper< ptr< Instruction > > ) >
( e, "DecoratorAddEpilogueInstr"_sid,
[]( const auto& pBB, auto& instr )
{
auto& bb = *pBB.get();
bb.addEpilogueInstr( *instr.get() );
} );
}
}
|
Changes to bs/g0api/extensibility/context.cpp.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
namespace goose::g0api
{
void SetupContextExtensibilityFuncs( Env& e )
{
wptr< Env > pEnv = e.shared_from_this();
RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< ptr< Context > > ) >( e, "ContextGetIdentity"_sid,
| | | 9 10 11 12 13 14 15 16 17 18 19 20 21 |
namespace goose::g0api
{
void SetupContextExtensibilityFuncs( Env& e )
{
wptr< Env > pEnv = e.shared_from_this();
RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< ptr< Context > > ) >( e, "ContextGetIdentity"_sid,
[]( const auto& c )
{
return c->identity();
} );
}
}
|
Changes to bs/g0api/extensibility/eir.cpp.
| ︙ | ︙ | |||
101 102 103 104 105 106 107 |
DefineConstant( e, "TermTypeVec"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Vec ) ) ) );
DefineConstant( e, "TermTypeBigInt"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::BigInt ) ) ) );
DefineConstant( e, "TermTypeFixedInt"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::FixedInt ) ) ) );
DefineConstant( e, "TermTypeInternal"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Internal ) ) ) );
// Functions
RegisterBuiltinFunc< BigInt ( TypeWrapper< Term > ) >( e, "GetTermType"_sid,
| | | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
DefineConstant( e, "TermTypeVec"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Vec ) ) ) );
DefineConstant( e, "TermTypeBigInt"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::BigInt ) ) ) );
DefineConstant( e, "TermTypeFixedInt"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::FixedInt ) ) ) );
DefineConstant( e, "TermTypeInternal"_sid, ValueToEIR( ToValue( static_cast< uint8_t >( TermType::Internal ) ) ) );
// Functions
RegisterBuiltinFunc< BigInt ( TypeWrapper< Term > ) >( e, "GetTermType"_sid,
[]( const auto& t )
{
return BigInt::FromU32( min< uint8_t >( t.get().index(), static_cast< uint8_t >( TermType::Internal ) ) );
} );
////////////////////////////
// MkTerm overloads
////////////////////////////
|
| ︙ | ︙ | |||
139 140 141 142 143 144 145 |
auto GetTermValue = CreateOverloadSet( e, "GetTermValue"_sid );
RegisterGetTermValueOverload< uint64_t >( e, GetTermValue );
RegisterGetTermValueOverload< TypeWrapper< LocationId > >( e, GetTermValue );
RegisterGetTermValueOverload< string >( e, GetTermValue );
RegisterGetTermValueOverload< TypeWrapper< StringId > >( e, GetTermValue );
RegisterBuiltinFunc< bool ( TypeWrapper< Term >, TermRef< uint8_t > ) >( e, "GetDelimiterTermValue"_sid,
| | | 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
auto GetTermValue = CreateOverloadSet( e, "GetTermValue"_sid );
RegisterGetTermValueOverload< uint64_t >( e, GetTermValue );
RegisterGetTermValueOverload< TypeWrapper< LocationId > >( e, GetTermValue );
RegisterGetTermValueOverload< string >( e, GetTermValue );
RegisterGetTermValueOverload< TypeWrapper< StringId > >( e, GetTermValue );
RegisterBuiltinFunc< bool ( TypeWrapper< Term >, TermRef< uint8_t > ) >( e, "GetDelimiterTermValue"_sid,
[]( const auto& t, auto& tref )
{
const auto* pVal = get_if< Delimiter >( &t.get() );
if( !pVal )
return false;
tref = static_cast< uint8_t >( *pVal );
return true;
|
| ︙ | ︙ | |||
161 162 163 164 165 166 167 |
RegisterGetTermValueOverload< TypeWrapper< APSInt > >( e, GetTermValue );
////////////////////////////
// LocationId
////////////////////////////
RegisterBuiltinFunc< TypeWrapper< LocationId > ( TypeWrapper< LocationId >, TypeWrapper< LocationId > ) >( e,
"MkSpanningLocation"_sid,
| | | | | | | | | | | | | | | | | | | | | | | | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
RegisterGetTermValueOverload< TypeWrapper< APSInt > >( e, GetTermValue );
////////////////////////////
// LocationId
////////////////////////////
RegisterBuiltinFunc< TypeWrapper< LocationId > ( TypeWrapper< LocationId >, TypeWrapper< LocationId > ) >( e,
"MkSpanningLocation"_sid,
[]( const auto& loc1, const auto& loc2 ) -> TypeWrapper< LocationId >
{
return static_cast< LocationId >( Location::CreateSpanningLocation( loc1.get(), loc2.get() ) );
} );
////////////////////////////
// StringId
////////////////////////////
RegisterBuiltinFunc< TypeWrapper< StringId > ( string ) >( e, "MkStringId"_sid,
[]( string s ) -> TypeWrapper< StringId >
{
return s;
} );
////////////////////////////
// Hole
////////////////////////////
RegisterBuiltinFunc< TypeWrapper< Hole > ( TypeWrapper< StringId > ) >( e, "MkHole"_sid,
[]( const auto& name ) -> TypeWrapper< Hole >
{
return Hole( name );
} );
RegisterBuiltinFunc< TypeWrapper< Hole > ( TypeWrapper< StringId >, TypeWrapper< Term > ) >( e, "MkHole"_sid,
[]( const auto& name, const auto& kind ) -> TypeWrapper< Hole >
{
return Hole( name, kind.get() );
} );
RegisterBuiltinFunc< TypeWrapper< StringId > ( TypeWrapper< Hole > ) >( e, "GetHoleName"_sid,
[]( const auto& h ) -> TypeWrapper< StringId >
{
return h.get().name();
} );
RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< Hole > ) >( e, "GetHoleKind"_sid,
[]( const auto& h ) -> TypeWrapper< Term >
{
return h.get().kind();
} );
////////////////////////////
// AnyTerm
////////////////////////////
RegisterBuiltinFunc< TypeWrapper< StringId > ( TypeWrapper< AnyTerm > ) >( e, "GetAnyTermVarName"_sid,
[]( const auto& at ) -> TypeWrapper< StringId >
{
return at.get().varName();
} );
RegisterBuiltinFunc< TypeWrapper< AnyTerm > ( TypeWrapper< StringId > ) >( e, "MkAnyTerm"_sid,
[]( const auto& name ) -> TypeWrapper< AnyTerm >
{
return AnyTerm( name );
} );
////////////////////////////
// VecOfLength
////////////////////////////
RegisterBuiltinFunc< TypeWrapper< StringId > ( TypeWrapper< VecOfLength > ) >( e, "GetVecOfLengthVarName"_sid,
[]( const auto& at ) -> TypeWrapper< StringId >
{
return at.get().varName();
} );
RegisterBuiltinFunc< TypeWrapper< VecOfLength > ( TypeWrapper< StringId > ) >( e, "MkVecOfLength"_sid,
[]( const auto& name ) -> TypeWrapper< VecOfLength >
{
return VecOfLength( name );
} );
////////////////////////////
// Vector
////////////////////////////
RegisterBuiltinFunc< TypeWrapper< pvec > () >( e, "MkVec"_sid,
[]() -> TypeWrapper< pvec >
{
return make_shared< Vector >();
} );
RegisterBuiltinFunc< void ( TypeWrapper< pvec >, TypeWrapper< pvec > ) >( e, "MkVecConcat"_sid,
[]( const auto& vec1, const auto& vec2 )
{
return make_shared< Vector >( Vector::MakeConcat( *vec1.get(), *vec2.get() ) );
} );
RegisterBuiltinFunc< void ( TypeWrapper< pvec >, uint32_t, TypeWrapper< Term > ) >( e, "SetTerm"_sid,
[]( const auto& vec, uint32_t index, const auto& t )
{
vec->terms()[index] = t.get();
} );
RegisterBuiltinFunc< void ( TypeWrapper< pvec >, int32_t ) >( e, "SetVecWeight"_sid,
[]( const auto& vec, int32_t w )
{
vec->setWeight( w );
} );
RegisterBuiltinFunc< void ( TypeWrapper< pvec >, int32_t ) >( e, "SetVecWeightOverride"_sid,
[]( const auto& vec, int32_t w )
{
vec->setWeightOverride( w );
} );
RegisterBuiltinFunc< void ( TypeWrapper< pvec >, TypeWrapper< Term > ) >( e, "VecAppend"_sid,
[]( const auto& vec, const auto& t )
{
vec->append( t.get() );
} );
RegisterBuiltinFunc< void ( TypeWrapper< pvec >, TypeWrapper< Term > ) >( e, "VecSetRepetition"_sid,
[]( const auto& vec, const auto& t )
{
vec->setRepetitionTerm( t.get() );
} );
RegisterBuiltinFunc< tuple< uint32_t, bool > ( TypeWrapper< pvec > ) >( e, "GetVecLength"_sid,
[]( const auto& vec )
{
auto vl = vec->length();
return make_tuple( static_cast< uint32_t >( vl.minLength() ), vl.isVariable() );
} );
RegisterBuiltinFunc< bool ( TypeWrapper< pvec >, uint32_t, TermRef< TypeWrapper< Term > > ) >( e, "GetVecTerm"_sid,
[]( const auto& vec, uint32_t index, auto& out )
{
const auto& v = *vec.get();
if( v.length().minLength() <= index )
return false;
out = v.terms()[index];
return true;
} );
RegisterBuiltinFunc< int32_t ( TypeWrapper< pvec > ) >( e, "GetVecWeight"_sid,
[]( const auto& vec )
{
return vec->weight();
} );
RegisterBuiltinFunc< int32_t ( TypeWrapper< pvec > ) >( e, "GetVecWeightOverride"_sid,
[]( const auto& vec )
{
return vec->weightOverride();
} );
RegisterBuiltinFunc< int32_t ( TypeWrapper< pvec >, TermRef< TypeWrapper< Term > > ) >( e, "GetVecRepetitionTerm"_sid,
[]( const auto& vec, auto& out )
{
const auto& rt = vec->repetitionTerm();
if( !rt )
return false;
out = *rt;
return true;
} );
RegisterBuiltinFunc< bool ( TypeWrapper< pvec > ) >( e, "IsVecEmpty"_sid,
[]( const auto& vec )
{
return vec->empty();
} );
////////////////////////////
// Helpers
////////////////////////////
RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< Term >, TypeWrapper< Term > ) >( e, "AppendToVectorTerm"_sid,
[]( const auto& vec, const auto& t ) -> TypeWrapper< Term >
{
return AppendToVectorTerm( vec, t );
} );
////////////////////////////
// Integers
////////////////////////////
|
| ︙ | ︙ | |||
361 362 363 364 365 366 367 |
ai.setIsSigned( false );
return ToValue( TypeWrapper< APSInt >( s ) );
} );
auto ToBigInt = CreateOverloadSet( e, "ToBigInt"_sid );
RegisterBuiltinFunc< Eager< BigInt > ( TypeWrapper< APSInt > ) >( e, ToBigInt,
| | | | | | 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 |
ai.setIsSigned( false );
return ToValue( TypeWrapper< APSInt >( s ) );
} );
auto ToBigInt = CreateOverloadSet( e, "ToBigInt"_sid );
RegisterBuiltinFunc< Eager< BigInt > ( TypeWrapper< APSInt > ) >( e, ToBigInt,
[]( const auto& fInt )
{
return fInt;
} );
RegisterBuiltinFunc< Eager< BigInt > ( char32_t ) >( e, ToBigInt,
[]( char32_t c )
{
return BigInt::FromU32( c );
} );
////////////////////////////
// Propositions
////////////////////////////
RegisterBuiltinFunc< bool ( TypeWrapper< ptr< Context > >, TypeWrapper< Value >, TermRef< TypeWrapper< ptr< Propositions > > > ) >( e, "GetTypePredicates"_sid,
[]( const auto& c, const auto& type, auto& out ) -> bool
{
if( !ParseTypePredicates( *c.get(), type.get() ) )
return false;
auto ppPreds = GetTypePredicates( type );
if( !ppPreds || !( *ppPreds ) )
return false;
out = *ppPreds;
return true;
} );
RegisterBuiltinFunc< uint32_t ( TypeWrapper< ptr< Propositions > > ) >( e, "GetPropositionsCount"_sid,
[]( const auto& preds )
{
return preds->props().size();
} );
RegisterBuiltinFunc< bool ( TypeWrapper< ptr< Propositions > >, uint32_t, TermRef< TypeWrapper< Value > > ) >( e, "GetProposition"_sid,
[]( const auto& preds, uint32_t index, auto& out )
{
if( preds->props().size() <= index )
return false;
out = preds->props()[index];
return true;
} );
}
}
|
Changes to bs/g0api/extensibility/value.cpp.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 |
////////////////////////////
// MkValue overloads
////////////////////////////
auto MkValue = CreateOverloadSet( e, "MkValue"_sid );
RegisterBuiltinFunc< TypeWrapper< Value > ( TypeWrapper< Term >, TypeWrapper< Term > ) >( e, MkValue,
| | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
////////////////////////////
// MkValue overloads
////////////////////////////
auto MkValue = CreateOverloadSet( e, "MkValue"_sid );
RegisterBuiltinFunc< TypeWrapper< Value > ( TypeWrapper< Term >, TypeWrapper< Term > ) >( e, MkValue,
[]( const auto& type, const auto& val ) -> TypeWrapper< Value >
{
return Value( type.get(), val.get() );
} );
RegisterBuiltinFunc< TypeWrapper< Value > ( TypeWrapper< Term > ) >( e, MkValue,
[]( const auto& type ) -> TypeWrapper< Value >
{
return Value( type.get(), make_shared< InstrSeq >() );
} );
auto AppendValueInstr = CreateOverloadSet( e, "AppendValueInstr"_sid );
RegisterBuiltinFunc< bool ( TypeWrapper< Value >, TypeWrapper< ptr< Instruction > > ) >( e, AppendValueInstr,
[]( auto& val, const auto& pInstr )
{
if( val.get().isConstant() )
return false;
val.get().cir()->emplace_back( *pInstr.get() );
return true;
} );
RegisterBuiltinFunc< bool ( TypeWrapper< Value >, TypeWrapper< ptr< InstrSeq > > ) >( e, AppendValueInstr,
[]( auto& val, const auto& pIS )
{
if( val.get().isConstant() )
return false;
for( auto&& x : *pIS.get() )
val.get().cir()->emplace_back( x );
return true;
} );
RegisterBuiltinFunc< bool ( TypeWrapper< Value >, TypeWrapper< Value > ) >( e, AppendValueInstr,
[]( auto& val, const auto& valToAppend )
{
if( val.get().isConstant() )
return false;
val.get().cir()->emplace_back( valToAppend.get() );
return true;
} );
RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< Value > ) >( e, "GetValueType"_sid,
[]( const auto& val ) -> TypeWrapper< Term >
{
return val.get().type();
} );
RegisterBuiltinFunc< bool ( TypeWrapper< Value >, TermRef< TypeWrapper< Term > > ) >( e, "GetValueVal"_sid,
[]( const auto& val, auto& out )
{
if( !val.get().isConstant() )
return false;
out = val.get().val();
return true;
} );
RegisterBuiltinFunc< bool ( TypeWrapper< Value >, TermRef< TypeWrapper< ptr< InstrSeq > > > ) >( e, "GetValueCIR"_sid,
[]( const auto& val, auto& out )
{
if( val.get().isConstant() )
return false;
out = val.get().cir();
return true;
} );
RegisterBuiltinFunc< TypeWrapper< LocationId > ( TypeWrapper< Value > ) >( e, "GetValueLocation"_sid,
[]( const auto& val ) -> TypeWrapper< LocationId >
{
return val.get().locationId();
} );
RegisterBuiltinFunc< bool ( TypeWrapper< Value > ) >( e, "IsPoisonValue"_sid,
[]( const auto& val )
{
return val.get().isPoison();
} );
RegisterBuiltinFunc< bool ( TypeWrapper< Value > ) >( e, "IsConstantValue"_sid,
[]( const auto& val )
{
return val.get().isConstant();
} );
RegisterBuiltinFunc< bool ( TypeWrapper< Value > ) >( e, "IsTypeValue"_sid,
[]( const auto& val )
{
return val.get().isType();
} );
RegisterBuiltinFunc< TypeWrapper< Value > ( TypeWrapper< Value >, TypeWrapper< LocationId > ) >( e, "SetValueLocation"_sid,
[]( const auto& v, const auto& loc ) -> TypeWrapper< Value >
{
Value val( v );
return val.setLocationId( loc );
} );
RegisterBuiltinFunc< TypeWrapper< Term > ( TypeWrapper< Value > ) >( e, "ValueToEIR"_sid,
[]( const auto& val ) -> TypeWrapper< Term >
{
return ValueToEIR( val.get() );
} );
RegisterBuiltinFunc< bool ( TypeWrapper< Term >, TermRef< TypeWrapper< Value > > ) >( e, "EIRToValue"_sid,
[]( const auto& t, auto& out )
{
auto val = EIRToValue( t.get() );
if( !val )
return false;
out = move( *val );
return true;
} );
RegisterBuiltinFunc< TypeWrapper< Value > ( CustomPattern< Value, ValuePatternT > ) >( e, "WrapValue"_sid,
[]( const auto& v ) -> TypeWrapper< Value >
{
return v;
} );
}
}
|
Changes to bs/g0api/types.cpp.
| ︙ | ︙ | |||
76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
////////////////////////////
// CIR types
////////////////////////////
SetupWrapperForType< ptr< CFG > >( e, "CFG"_sid, pEquals, pNotEquals );
SetupWrapperForType< ptr< BasicBlock > >( e, "BasicBlock"_sid, pEquals, pNotEquals );
SetupWrapperForType< ptr< Instruction > >( e, "Instruction"_sid, pEquals, pNotEquals );
SetupWrapperForType< ptr< Terminator > >( e, "Terminator"_sid, pEquals, pNotEquals );
SetupWrapperForType< ptr< Decorator > >( e, "Decorator"_sid, pEquals, pNotEquals );
////////////////////////////
// Context
////////////////////////////
SetupWrapperForType< ptr< Context > >( e, "Context"_sid, pEquals, pNotEquals );
| > | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
////////////////////////////
// CIR types
////////////////////////////
SetupWrapperForType< ptr< CFG > >( e, "CFG"_sid, pEquals, pNotEquals );
SetupWrapperForType< ptr< BasicBlock > >( e, "BasicBlock"_sid, pEquals, pNotEquals );
SetupWrapperForType< ptr< Instruction > >( e, "Instruction"_sid, pEquals, pNotEquals );
SetupWrapperForType< ptr< InstrSeq > >( e, "InstrSeq"_sid, pEquals, pNotEquals );
SetupWrapperForType< ptr< Terminator > >( e, "Terminator"_sid, pEquals, pNotEquals );
SetupWrapperForType< ptr< Decorator > >( e, "Decorator"_sid, pEquals, pNotEquals );
////////////////////////////
// Context
////////////////////////////
SetupWrapperForType< ptr< Context > >( e, "Context"_sid, pEquals, pNotEquals );
|
| ︙ | ︙ |
Changes to bs/g0api/types.h.
| ︙ | ︙ | |||
69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
};
template<>
struct TypeWrapperTraits< ptr< cir::Instruction > >
{
static auto typeId() { return "Instruction"_sid; }
};
template<>
struct TypeWrapperTraits< ptr< cir::Terminator > >
{
static auto typeId() { return "Terminator"_sid; }
};
| > > > > > > | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
};
template<>
struct TypeWrapperTraits< ptr< cir::Instruction > >
{
static auto typeId() { return "Instruction"_sid; }
};
template<>
struct TypeWrapperTraits< ptr< cir::InstrSeq > >
{
static auto typeId() { return "InstrSeq"_sid; }
};
template<>
struct TypeWrapperTraits< ptr< cir::Terminator > >
{
static auto typeId() { return "Terminator"_sid; }
};
|
| ︙ | ︙ |
Changes to bs/parse/bracketblock.cpp.
| ︙ | ︙ | |||
128 129 130 131 132 133 134 |
// Create a new identity for the predicates, which imports everything from the parent identity
// and also makes @val visible as a placeholder for the type's value.
preds->setIdentity( VEC( Env::NewUniqueId() ) );
c.env()->addVisibilityRule( c.identity(), preds->identity() );
auto name = "@val"_sid;
auto typeTerm = ValueToEIR( type );
| | | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
// Create a new identity for the predicates, which imports everything from the parent identity
// and also makes @val visible as a placeholder for the type's value.
preds->setIdentity( VEC( Env::NewUniqueId() ) );
c.env()->addVisibilityRule( c.identity(), preds->identity() );
auto name = "@val"_sid;
auto typeTerm = ValueToEIR( type );
auto phVal = ValueToEIR( BuildComputedValue( typeTerm, cir::Placeholder( typeTerm, name, {} ) ) );
auto valuePlaceholderIdentity = AppendToVectorTerm( preds->identity(), TERM( name ) );
c.env()->storeValue( valuePlaceholderIdentity, ANYTERM( _ ), phVal );
// The placeholder is also available as the default lhs value, so "@val" can be omitted in many cases
auto defLhsIdentity = AppendToVectorTerm( preds->identity(), TSID( 0_def_lhs ) );
c.env()->storeValue( defLhsIdentity, ANYTERM( _ ), move( phVal ) );
|
| ︙ | ︙ |
Changes to bs/parse/parser.inl.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 |
{
DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );
builtins::PoisonBuilder( context() );
}
flushValue();
| < < | < | | < < < < < | < < | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
{
DiagnosticsManager::GetInstance().setCurrentVerbosityLevel( Verbosity::Silent );
builtins::PoisonBuilder( context() );
}
flushValue();
m_lastValue = forward< V >( val );
if( !context().locationId().invalid() )
m_lastValue->setLocationId( context().locationId() );
// If the value we just pushed is of void type, it was pushed only for its side effects
// (for instance a SetVar instruction), so it should be flushed immediately.
if( m_lastValue->type() == GetValueType< void >() )
flushValue();
}
template< typename T >
optional< uint32_t > Parser::getPrecedence( const Term&, const T& )
{
return nullopt;
|
| ︙ | ︙ |
Changes to bs/parse/rule-helpers.h.
| ︙ | ︙ | |||
20 21 22 23 24 25 26 |
void BuildParseRule( sema::Env& env, StringId name, const Term& identity, R&&... ruleBuilders )
{
Rule r;
( ( ruleBuilders( env, r, name ) ), ... );
RegisterRule( env, identity, move( r ) );
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
void BuildParseRule( sema::Env& env, StringId name, const Term& identity, R&&... ruleBuilders )
{
Rule r;
( ( ruleBuilders( env, r, name ) ), ... );
RegisterRule( env, identity, move( r ) );
}
template< typename F >
void MakePrefixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func );
template< typename F >
void MakePostfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func );
template< typename F >
|
| ︙ | ︙ |
Changes to bs/parse/rule-helpers.inl.
| ︙ | ︙ | |||
35 36 37 38 39 40 41 42 |
p.pushValue( PoisonValue() );
return true;
}
DiagnosticsContext dc( locationId, false );
auto loc = Location::CreateSpanningLocation( locationId, rightVal->locationId() );
auto result = func( p, move( *rightVal ) );
| > > | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
p.pushValue( PoisonValue() );
return true;
}
DiagnosticsContext dc( locationId, false );
auto loc = Location::CreateSpanningLocation( locationId, rightVal->locationId() );
sema::Context::CurrentLocationGuard clg( p.context(), loc );
auto result = func( p, move( *rightVal ) );
p.pushValue( move( result ) );
return true;
} );
}
template< typename F >
void MakePostfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
{
|
| ︙ | ︙ | |||
65 66 67 68 69 70 71 72 |
p.pushValue( PoisonValue() );
return true;
}
DiagnosticsContext dc( locationId, false );
auto loc = Location::CreateSpanningLocation( leftVal->locationId(), locationId );
auto result = func( p, move( *leftVal ) );
| > > | < | 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
p.pushValue( PoisonValue() );
return true;
}
DiagnosticsContext dc( locationId, false );
auto loc = Location::CreateSpanningLocation( leftVal->locationId(), locationId );
sema::Context::CurrentLocationGuard clg( p.context(), loc );
auto result = func( p, move( *leftVal ) );
p.pushValue( move( result ) );
return true;
} );
}
template< typename F >
void MakeLeftAssInfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
{
|
| ︙ | ︙ | |||
108 109 110 111 112 113 114 115 |
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
"expected an expression.", 0 );
return false;
}
}
auto loc = Location::CreateSpanningLocation( locationId, rightVal->locationId() );
auto result = func( p, *EIRToValue( *defLhsVal ), move( *rightVal ) );
| > > | | 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
"expected an expression.", 0 );
return false;
}
}
auto loc = Location::CreateSpanningLocation( locationId, rightVal->locationId() );
sema::Context::CurrentLocationGuard clg( p.context(), loc );
auto result = func( p, *EIRToValue( *defLhsVal ), move( *rightVal ) );
p.pushValue( move( result ) );
return true;
} );
}
r.setInfixFunc(
[=]( const Parser& p ) { return precedence; },
[=]( Parser& p, LocationId locationId, uint32_t prec )
|
| ︙ | ︙ | |||
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
DiagnosticsContext dcl( leftVal->locationId(), false );
DiagnosticsContext dcr( rightVal->locationId(), false );
DiagnosticsContext dc( locationId, false );
auto loc = Location::CreateSpanningLocation( leftVal->locationId(), rightVal->locationId() );
// If there is a default lhs value available in our current context, fail silently
// and put back the leftVal. The parsing will then end and drop the current expression (leftVal), and
// try parsing this again as a prefix operator with the default lhs value. That's not hackish at all, honest
auto defLhsVal = p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
if( defLhsVal )
{
VerbosityContext vc( Verbosity::Silent );
auto result = func( p, *leftVal, move( *rightVal ) );
if( result.isPoison() )
return false;
| > | | | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
DiagnosticsContext dcl( leftVal->locationId(), false );
DiagnosticsContext dcr( rightVal->locationId(), false );
DiagnosticsContext dc( locationId, false );
auto loc = Location::CreateSpanningLocation( leftVal->locationId(), rightVal->locationId() );
sema::Context::CurrentLocationGuard clg( p.context(), loc );
// If there is a default lhs value available in our current context, fail silently
// and put back the leftVal. The parsing will then end and drop the current expression (leftVal), and
// try parsing this again as a prefix operator with the default lhs value. That's not hackish at all, honest
auto defLhsVal = p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
if( defLhsVal )
{
VerbosityContext vc( Verbosity::Silent );
auto result = func( p, *leftVal, move( *rightVal ) );
if( result.isPoison() )
return false;
p.pushValue( move( result ) );
return true;
}
auto result = func( p, move( *leftVal ), move( *rightVal ) );
p.pushValue( move( result ) );
return true;
} );
}
template< typename F >
void MakeRightAssInfixOp( sema::Env& env, Rule& r, StringId name, uint32_t precedence, F&& func )
{
|
| ︙ | ︙ | |||
213 214 215 216 217 218 219 220 |
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
"expected an expression.", 0 );
return false;
}
}
auto loc = Location::CreateSpanningLocation( locationId, rightVal->locationId() );
auto result = func( p, *EIRToValue( *defLhsVal ), move( *rightVal ) );
| > > | | 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
DiagnosticsManager::GetInstance().emitSyntaxErrorMessage( locationId,
"expected an expression.", 0 );
return false;
}
}
auto loc = Location::CreateSpanningLocation( locationId, rightVal->locationId() );
sema::Context::CurrentLocationGuard clg( p.context(), loc );
auto result = func( p, *EIRToValue( *defLhsVal ), move( *rightVal ) );
p.pushValue( move( result ) );
return true;
} );
}
r.setInfixFunc(
[=]( const Parser& p ) { return precedence; },
[=]( Parser& p, LocationId locationId, uint32_t prec )
|
| ︙ | ︙ | |||
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 |
DiagnosticsContext dcl( leftVal->locationId(), false );
DiagnosticsContext dcr( rightVal->locationId(), false );
DiagnosticsContext dc( locationId, false );
auto loc = Location::CreateSpanningLocation( leftVal->locationId(), rightVal->locationId() );
auto defLhsVal = p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
if( defLhsVal )
{
VerbosityContext vc( Verbosity::Silent );
auto result = func( p, move( *leftVal ), move( *rightVal ) );
if( result.isPoison() )
return false;
| > | | | 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
DiagnosticsContext dcl( leftVal->locationId(), false );
DiagnosticsContext dcr( rightVal->locationId(), false );
DiagnosticsContext dc( locationId, false );
auto loc = Location::CreateSpanningLocation( leftVal->locationId(), rightVal->locationId() );
sema::Context::CurrentLocationGuard clg( p.context(), loc );
auto defLhsVal = p.context().env()->retrieveValue( "0_def_lhs"_sid, p.context().identity() );
if( defLhsVal )
{
VerbosityContext vc( Verbosity::Silent );
auto result = func( p, move( *leftVal ), move( *rightVal ) );
if( result.isPoison() )
return false;
p.pushValue( move( result ) );
return true;
}
auto result = func( p, move( *leftVal ), move( *rightVal ) );
p.pushValue( move( result ) );
return true;
} );
}
}
#endif
|
Changes to bs/sema/context.h.
| ︙ | ︙ | |||
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
m_pEnv = pEnv;
}
void setIdentity( const Term& identity )
{
m_identity = identity;
}
private:
ptr< Env > m_pEnv;
Term m_identity;
optional< Term > m_returnType;
Value m_builder;
};
}
#endif
| > > > > > > > > > > > > > > > > > > > > > > > > | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
m_pEnv = pEnv;
}
void setIdentity( const Term& identity )
{
m_identity = identity;
}
auto locationId() const { return m_currentLoc; }
class CurrentLocationGuard
{
public:
CurrentLocationGuard( Context& c, LocationId loc ) :
m_context( c ),
m_previousLoc( c.m_currentLoc )
{
c.m_currentLoc = loc;
}
~CurrentLocationGuard()
{
m_context.m_currentLoc = m_previousLoc;
}
private:
Context& m_context;
LocationId m_previousLoc;
};
private:
ptr< Env > m_pEnv;
Term m_identity;
optional< Term > m_returnType;
Value m_builder;
LocationId m_currentLoc;
};
}
#endif
|
Changes to bs/sema/lower.cpp.
| ︙ | ︙ | |||
23 24 25 26 27 28 29 |
optional< Value > LowerConstantForRuntime( const Context& c, const Value& val )
{
if( val.type() == GetValueType< bool >() || IsRuntimeType( *EIRToValue( val.type() ) ) )
return val;
DiagnosticsContext dc( val.locationId(), "When invoking LowerConstantForRuntime." );
auto result = InvokeOverloadSet( c, c.env()->extLowerConstantForRuntime(), AppendToTuple( EmptyTuple(), val ) );
| | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
optional< Value > LowerConstantForRuntime( const Context& c, const Value& val )
{
if( val.type() == GetValueType< bool >() || IsRuntimeType( *EIRToValue( val.type() ) ) )
return val;
DiagnosticsContext dc( val.locationId(), "When invoking LowerConstantForRuntime." );
auto result = InvokeOverloadSet( c, c.env()->extLowerConstantForRuntime(), AppendToTuple( EmptyTuple(), val ) );
if( result.isPoison() )
return nullopt;
return result;
}
optional< Value > LowerTypeForVerification( const Context& c, const Value& type )
{
|
| ︙ | ︙ |
Changes to bs/sema/tests/tctrie-merge.cpp.
1 2 3 | #define CATCH_CONFIG_MAIN #include "catch2/catch.hpp" #include "sema/sema.h" | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "sema/sema.h"
using namespace std;
using namespace goose;
using namespace goose::eir;
using namespace goose::sema;
SCENARIO( "TCTrie merge works", "[tctrie-merge]" )
{
WHEN( "Merging various expressions into an typechecking trie" )
{
auto vec1 = Vector::Make( TSTR( "foo" ), TSTR( "bar" ) );
|
| ︙ | ︙ |
Changes to bs/sema/tests/tctrie-typecheck.cpp.
1 2 3 | #define CATCH_CONFIG_MAIN #include "catch2/catch.hpp" #include "sema/sema.h" | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "sema/sema.h"
using namespace std;
using namespace goose;
using namespace goose::eir;
using namespace goose::sema;
namespace
{
auto GetSortedSolutions( const ptr< Env >& e, ptr< TCTrie< string > >& trie, const Vector& vec )
{
Context ctxt( e, VEC( TSID( g0 ) ) );
TypeCheckingContext context( ctxt );
|
| ︙ | ︙ |
Changes to bs/sema/tests/unify-holes.cpp.
1 2 3 | #define CATCH_CONFIG_MAIN #include "catch2/catch.hpp" #include "sema/sema.h" | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
#include "sema/sema.h"
using namespace std;
using namespace goose;
using namespace goose::eir;
using namespace goose::sema;
namespace
{
// Verifies that the unification of lhs and rhs yields only one solution, that it is complete,
// and that this solution is the expected one.
void CheckForUniqueSolution( const Term& lhs, const Term& rhs, const Term& expectedSolution, const TypeCheckingScore& expectedScore )
{
|
| ︙ | ︙ |
Changes to bs/util/util.h.
| ︙ | ︙ | |||
74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
namespace goose::util
{
template< typename T >
auto ComputeHash( const T& x )
{
return std::hash< T >()( x );
}
template< typename T >
class NullInit
{
public:
NullInit() {}
| > > > > > > > | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
namespace goose::util
{
template< typename T >
auto ComputeHash( const T& x )
{
return std::hash< T >()( x );
}
template< typename C >
Generator< uint64_t > ContainerHashGenerator( const C& c )
{
for( const auto& x : c )
co_yield ComputeHash( x );
}
template< typename T >
class NullInit
{
public:
NullInit() {}
|
| ︙ | ︙ |
Changes to bs/verify/basicblock.cpp.
| ︙ | ︙ | |||
37 38 39 40 41 42 43 |
{
m_currentBBPredicate = c.bool_const( format( "b{}", bbid ).c_str() );
m_builder.add( *m_currentBBPredicate == *pred );
}
m_builder.setCurrentPredicate( m_currentBBPredicate );
| | > | > | > > | | | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
{
m_currentBBPredicate = c.bool_const( format( "b{}", bbid ).c_str() );
m_builder.add( *m_currentBBPredicate == *pred );
}
m_builder.setCurrentPredicate( m_currentBBPredicate );
for( auto&& x : bb.instructions() )
{
if( !BuildZ3Op( m_builder, x ) )
m_builder.setcheckFailed();
}
// We pass the non-remapped index here because handleTerminator will do its own remapping.
if( bb.terminator() )
handleTerminator( bb.index(), *bb.terminator() );
return true;
}
}
|
Changes to bs/verify/builder.cpp.
| ︙ | ︙ | |||
70 71 72 73 74 75 76 |
}
optional< Z3Val > Builder::retrieveVar( uint32_t index, uint32_t bbIndex )
{
return m_varTracker.retrieve( *this, bbIndex, index );
}
| | < < < < < < < < < < < < | | | | | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
}
optional< Z3Val > Builder::retrieveVar( uint32_t index, uint32_t bbIndex )
{
return m_varTracker.retrieve( *this, bbIndex, index );
}
optional< Z3Val > Builder::setVar( uint32_t index, Z3Val&& v )
{
return m_varTracker.set( *this, index, move( v ) );
}
optional< Z3Val > Builder::retrieveGFC( const GhostFuncApplication& gfa, uint32_t bbIndex )
{
return m_gfcTracker.retrieve( *this, bbIndex, gfa );
}
void Builder::setGFC( const GhostFuncApplication& gfa, Z3Val&& v )
{
return m_gfcTracker.set( *this, gfa, move( v ) );
}
|
Changes to bs/verify/builder.h.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 |
using AssHandler = function< bool ( const z3::expr&, const z3::expr&, LocationId ) >;
// Helper class that tracks the states of a few things
// to build z3 expressions from a cfg.
class Builder
{
public:
| | | > > | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
using AssHandler = function< bool ( const z3::expr&, const z3::expr&, LocationId ) >;
// Helper class that tracks the states of a few things
// to build z3 expressions from a cfg.
class Builder
{
public:
Builder( const sema::Context& c, z3::solver& solver, size_t numVars, Remapper* remapper = nullptr ) :
m_context( &c ),
m_solver( &solver ),
m_remapper( remapper ),
m_varTracker( numVars ),
m_gfcTracker( numVars )
{
auto& zc = GetZ3Context();
// BasicBlock 1 is always considered to be the entry point, as such its predicate value
// is always assumed true.
add( zc.bool_const( "b1" ) == zc.bool_val( true ) );
}
|
| ︙ | ︙ | |||
53 54 55 56 57 58 59 60 61 |
void add( const z3::expr& e );
void assume( const z3::expr& e );
bool checkAssertion( const z3::expr& e, LocationId locationId );
void setLoadMustAssume( bool lma ) { m_loadMustAssume = lma; }
bool mustLoadAssume() const { return m_loadMustAssume; }
optional< Z3Val > retrieveVar( uint32_t index, uint32_t bbIndex = ~0 );
| > > > > > | < | | > > > > > > > > > > > > > | 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
void add( const z3::expr& e );
void assume( const z3::expr& e );
bool checkAssertion( const z3::expr& e, LocationId locationId );
void setLoadMustAssume( bool lma ) { m_loadMustAssume = lma; }
bool mustLoadAssume() const { return m_loadMustAssume; }
void resetVarStorage( uint32_t size )
{
m_varTracker = VarTracker( size );
}
optional< Z3Val > retrieveVar( uint32_t index, uint32_t bbIndex = ~0 );
optional< Z3Val > setVar( uint32_t index, Z3Val&& v );
optional< Z3Val > retrieveGFC( const GhostFuncApplication& gfa, uint32_t bbIndex = ~0 );
void setGFC( const GhostFuncApplication& gfa, Z3Val&& v );
const z3::expr* retrievePlaceholder( StringId sid ) const;
void setPlaceholder( StringId sid, const z3::expr& expr );
void unsetPlaceholder( StringId sid );
uint32_t newUniqueId() { return m_nextUniqueId++; }
bool hasCheckFailed() const { return m_checkFailed; }
void setcheckFailed() { m_checkFailed = true; }
template< typename T >
void push( T&& val )
{
m_stack.push( forward< T >( val ) );
}
template< typename T = Z3Val >
optional< T > pop()
{
return m_stack.pop< T >( *this );
}
template< typename F >
void setAssertionHandler( F&& handler )
{
m_assertionHandler = forward< F >( handler );
}
auto assertionHandler() const { return m_assertionHandler; }
|
| ︙ | ︙ | |||
97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
AssHandler m_assertionHandler;
unordered_map< StringId, z3::expr > m_placeholders;
VarTracker m_varTracker;
GFCTracker m_gfcTracker;
// TODO: investigate why this not being static breaks things, since it should
// be saved and restored whenever the solver is, but apparently it causes name collisions
// that invalidate the formulas. This works fine like this but it makes z3 formulas dumps
// less readable (due to super large variable name indices) and dependent on function checking order.
static uint32_t m_nextUniqueId;
| > > | 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
AssHandler m_assertionHandler;
unordered_map< StringId, z3::expr > m_placeholders;
VarTracker m_varTracker;
GFCTracker m_gfcTracker;
Stack m_stack;
// TODO: investigate why this not being static breaks things, since it should
// be saved and restored whenever the solver is, but apparently it causes name collisions
// that invalidate the formulas. This works fine like this but it makes z3 formulas dumps
// less readable (due to super large variable name indices) and dependent on function checking order.
static uint32_t m_nextUniqueId;
|
| ︙ | ︙ |
Changes to bs/verify/call.cpp.
1 2 3 4 5 6 7 8 |
#include "verify.h"
#include "builtins/builtins.h"
#include "helpers.inl"
using namespace goose::diagnostics;
namespace goose::verify
{
| | > > > > > > > > | > > | > > > > > | > > | > > > | > | | | > > < | | < < | | < | | | | | < | | | > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
#include "verify.h"
#include "builtins/builtins.h"
#include "helpers.inl"
using namespace goose::diagnostics;
namespace goose::verify
{
bool BuildZ3Op( Builder& b, const Call& instr )
{
auto fVal = b.pop< Value >();
if( !fVal )
return false;
auto rt = builtins::GetFuncRType( *fVal );
if( !rt )
return false;
auto func = FromValue< builtins::Func >( *fVal );
if( !func )
{
if( rt == GetValueType< void >() )
return true;
auto result = BuildZ3ConstantFromType( b, *rt, format( "r{}", b.newUniqueId() ) );
b.push( move( result ) );
return true;
}
const auto& ft = func->type();
const auto& fvi = ft.verifInfos();
if( !fvi )
return false;
optional< Z3Val > retExpr;
if( rt != GetValueType< void >() )
retExpr = BuildZ3ConstantFromType( b, ft.returnType(), format( "r{}", b.newUniqueId() ) );
// Create a temporary builder to construct the z3 expressions out of the
// function's pre-conditions and postconditions, configured
// to perform the necessary replacements for arguments and for the
// return value placeholder.
Builder cb = b;
auto fcir = func->cir();
if( fcir && fcir->body() )
cb.resetVarStorage( fcir->body()->temporariesCount() );
else
cb.resetVarStorage( 0 );
// Inject the arguments in the context.
// They will be picked up by getVars instructions when refered to
// by the verification conditions.
uint32_t argCount = instr.numArgs();
for( uint32_t argIndex = 0; argIndex < argCount; ++argIndex )
{
auto zv = b.pop();
if( !zv )
return false;
cb.setVar( argCount - argIndex - 1, move( *zv ) );
}
// Asserts the parameter types's predicates.
uint32_t varId = 0;
ForEachInVectorTerm( ft.params(), [&]( auto&& param )
{
auto result = Decompose( param,
Vec(
Lit( "value"_sid ),
Lit( "param"_sid ),
SubTerm(),
SubTerm(),
Val< LocationId >()
)
);
if( !result )
return true;
auto&& [type, val, locId] = *result;
auto paramVal = BuildComputedValue( type, VarAddr( varId++, type, locId ), Load( type, locId ) );
auto zv = BuildZ3ExprFromValue( cb, paramVal );
ForEachPredicate( cb, type, zv.expr, [&]( auto&& z3expr, auto locId )
{
DiagnosticsContext dc( fVal->locationId(), "At this call." );
b.checkAssertion( z3expr, locId );
} );
return true;
} );
// Setup the return value placeholder
if( retExpr )
cb.setPlaceholder( "@result"_sid, retExpr->expr );
// Check preconditions.
const auto& preConds = fvi->preConds();
preConds->forEach( [&]( auto&& val )
{
if( auto zv = TryBuildZ3ExprFromValue( cb, val ) )
{
DiagnosticsContext dc( fVal->locationId(), "At this call." );
b.checkAssertion( zv->expr, val.locationId() );
}
} );
// Add the return type's predicates as assumptions.
ForEachPredicate( cb, ft.returnType(), retExpr->expr, [&]( auto&& z3expr, auto locId )
{
b.assume( z3expr );
} );
// Add postconditions as assumptions.
const auto& postConds = fvi->postConds();
postConds->forEach( [&]( auto&& val )
{
if( auto zv = TryBuildZ3ExprFromValue( cb, val ) )
b.assume( zv->expr );
} );
if( retExpr )
b.push( move( *retExpr ) );
return true;
}
}
|
Changes to bs/verify/cfg.cpp.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
bool Func::buildZ3Expressions( const cir::BasicBlock& bb, queue< const BasicBlock* >* parentWorkQueue )
{
uint32_t currentLoopId = m_remapper.getCurrentLoopId();
queue< const BasicBlock* > workQueue;
workQueue.push( &bb );
| | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
bool Func::buildZ3Expressions( const cir::BasicBlock& bb, queue< const BasicBlock* >* parentWorkQueue )
{
uint32_t currentLoopId = m_remapper.getCurrentLoopId();
queue< const BasicBlock* > workQueue;
workQueue.push( &bb );
while( !workQueue.empty() )
{
const auto* pBB = workQueue.front();
workQueue.pop();
if( currentLoopId && pBB->loopId() != currentLoopId && pBB->index() != currentLoopId )
{
// If we encounter a successor BB that belongs to a different loop than the
|
| ︙ | ︙ | |||
31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
continue;
}
// If we encounter a loop header for a different
// loop than the current one, check the loop.
if( pBB->isLoopHeader() && pBB->index() != currentLoopId )
{
if( !checkLoop( *pBB, workQueue ) )
return false;
continue;
}
// Make sure that every predecessor have been processed first.
| > > > > | 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
continue;
}
// If we encounter a loop header for a different
// loop than the current one, check the loop.
if( pBB->isLoopHeader() && pBB->index() != currentLoopId )
{
// If we encounter a loop and a check previously failed, bail out
if( m_builder.hasCheckFailed() )
return false;
if( !checkLoop( *pBB, workQueue ) )
return false;
continue;
}
// Make sure that every predecessor have been processed first.
|
| ︙ | ︙ |
Deleted bs/verify/comptime.cpp.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to bs/verify/func.cpp.
| ︙ | ︙ | |||
13 14 15 16 17 18 19 |
Func::Func( const sema::Context& c, const builtins::Func& func ) :
m_func( &func ),
m_cfg( func.cir()->body() ),
m_retType( func.type().returnType() ),
m_solver( GetZ3Context(), z3::solver::simple() ),
m_remapper( m_cfg->count() + 1 ),
| | | > > > > > > | 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
Func::Func( const sema::Context& c, const builtins::Func& func ) :
m_func( &func ),
m_cfg( func.cir()->body() ),
m_retType( func.type().returnType() ),
m_solver( GetZ3Context(), z3::solver::simple() ),
m_remapper( m_cfg->count() + 1 ),
m_builder( c, m_solver, m_cfg->temporariesCount(), &m_remapper )
{}
Func::Func( const sema::Context& c, const ptr< cir::CFG >& cfg, const Term& retType ) :
m_cfg( cfg ),
m_retType( retType ),
m_solver( GetZ3Context(), z3::solver::simple() ),
m_remapper( m_cfg->count() + 1 ),
m_builder( c, m_solver, m_cfg->temporariesCount(), &m_remapper )
{}
bool Func::verify()
{
ProfileZoneScoped;
if( m_func )
m_viz.setFunctionIdentity( m_func->cir()->identity() );
ComputeLoops( m_cfg );
ComputeLoopModifiedAddrs( m_cfg );
try
{
// Force the old z3 arith solver for now as there is apparently an issue
// with arith and bitvector (https://github.com/Z3Prover/z3/issues/5106)
// This prevents verification-c-fail-test-8 from hanging with recent versions
// of z3
m_solver.set( "arith.solver", 2U );
m_builder.setTraceMode( ms_TraceMode );
if( ms_TraceMode )
cout << "=== Begin function verification trace ===\n";
m_builder.setAssertionHandler( [&]( auto&& expr, auto&& exprToCheck, LocationId locationId )
{
return checkAssertion( expr, exprToCheck, locationId );
|
| ︙ | ︙ | |||
68 69 70 71 72 73 74 |
)
);
if( !result )
return true;
auto&& [type, val, locId] = *result;
| | > | | | < | | > | < | | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
)
);
if( !result )
return true;
auto&& [type, val, locId] = *result;
auto paramVal = BuildComputedValue( type, VarAddr( varId, type, locId ), Load( type, locId ) )
.setLocationId( locId );
// Initialize every parameter containing variable with a freshly named constant of the right type.
auto paramInit = BuildZ3ConstantFromType( m_builder, type, format( "p{}", varId ) );
m_builder.setVar( varId, move( paramInit ) );
++varId;
auto zv = BuildZ3ExprFromValue( m_builder, paramVal );
ForEachPredicate( m_builder, type, zv.expr, [&]( auto&& z3expr, auto locId )
{
if( z3expr.is_bool() )
m_builder.assume( z3expr );
} );
return true;
} );
// Add assumptions of the function's requirements
const auto& reqs = m_func->type().verifInfos()->preConds();
reqs->forEach( [&]( auto&& val )
{
// Don't do any error handling here, it should already have been taken care of
// by the condition verifier.
if( auto zv = TryBuildZ3ExprFromValue( m_builder, val ) )
m_builder.assume( zv->expr );
} );
}
bool result = buildZ3Expressions( *m_cfg->entryBB(), nullptr );
if( ms_TraceMode )
|
| ︙ | ︙ |
Changes to bs/verify/func.h.
| ︙ | ︙ | |||
32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
template< typename T >
bool handleTerminator( uint32_t bbIndex, const T& t )
{
return true;
}
bool handleTerminator( uint32_t bbIndex, const cir::Ret& t );
bool handleTerminator( uint32_t bbIndex, const cir::Branch& t );
bool handleTerminator( uint32_t bbIndex, const cir::CondBranch& t );
bool handleTerminator( uint32_t bbIndex, const cir::GhostBranch& t );
z3::expr makeEdgeExpression( uint32_t srcBBId, uint32_t destBBId, const z3::expr& cond );
| > | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
template< typename T >
bool handleTerminator( uint32_t bbIndex, const T& t )
{
return true;
}
bool handleTerminator( uint32_t bbIndex, const cir::RetVoid& t );
bool handleTerminator( uint32_t bbIndex, const cir::Ret& t );
bool handleTerminator( uint32_t bbIndex, const cir::Branch& t );
bool handleTerminator( uint32_t bbIndex, const cir::CondBranch& t );
bool handleTerminator( uint32_t bbIndex, const cir::GhostBranch& t );
z3::expr makeEdgeExpression( uint32_t srcBBId, uint32_t destBBId, const z3::expr& cond );
|
| ︙ | ︙ |
Changes to bs/verify/gfctracker.cpp.
1 2 3 4 5 6 | #include "verify.h" #include "builtins/builtins.h" using namespace goose; using namespace goose::verify; | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include "verify.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::verify;
uint32_t GFCTracker::getGFIndex( const GhostFuncApplication& gfa )
{
uint32_t gfIndex = 0;
auto it = m_ghostFuncIndices.find( gfa.func() );
if( it != m_ghostFuncIndices.end() )
gfIndex = it->second;
else
{
gfIndex = m_ghostFuncIndices.size();
m_ghostFuncIndices.emplace( gfa.func(), gfIndex );
}
return gfIndex;
}
z3::expr GFCTracker::buildFunctionApplication( Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const Value& gf )
{
|
| ︙ | ︙ | |||
39 40 41 42 43 44 45 |
SubTerm(),
Val< LocationId >()
)
);
assert( result );
auto&& [sort, type, val, locId] = *result;
| | | | | < | < < | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
SubTerm(),
Val< LocationId >()
)
);
assert( result );
auto&& [sort, type, val, locId] = *result;
z3args.emplace_back( BuildZ3ConstantFromType( b, type, format( "gca{}_{}", argIndex, uid ) ).expr );
++argIndex;
return true;
} );
return gfDecl.z3decl( z3args.size(), z3args.data() );
}
z3::expr GFCTracker::buildFunctionApplication( Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const GhostFuncApplication& gfa )
{
llvm::SmallVector< z3::expr, 8 > z3args;
z3args.reserve( gfa.args().size() + 1 );
z3args.emplace_back( GetZ3Context().int_val( uid ) );
for( auto&& zv : gfa.args() )
z3args.emplace_back( zv.expr );
return gfDecl.z3decl( z3args.size(), z3args.data() );
}
uint32_t GFCTracker::getCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex ) const
{
const auto* gfcs = m_gfcStorage.get( gfIndex );
|
| ︙ | ︙ | |||
81 82 83 84 85 86 87 |
void GFCTracker::setCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex, uint32_t uid )
{
auto* gfcs = m_gfcStorage.get( gfIndex );
if( !gfcs )
{
GFCState newGFCS;
newGFCS.resize( bbIndex + 1, 0 );
| | > | | | > | | < < | < < < | | | < < < > | | | | | | > | | | | < < | < < < > | | > | | | | | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
void GFCTracker::setCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex, uint32_t uid )
{
auto* gfcs = m_gfcStorage.get( gfIndex );
if( !gfcs )
{
GFCState newGFCS;
newGFCS.resize( bbIndex + 1, 0 );
auto& gfcsRef = m_gfcStorage.set( gfIndex, move( newGFCS ) );
gfcs = &gfcsRef;
}
else if( gfcs->size() <= bbIndex )
gfcs->resize( bbIndex + 1, 0 );
(*gfcs)[ bbIndex ] = uid;
}
void GFCTracker::setForBasicBlock( Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa,
uint32_t gfIndex, const GhostFuncDeclCache::GFDecl& gfDecl, z3::expr&& expr )
{
uint32_t oldUid = getCurrentUidForBasicBlock( bbIndex, gfIndex );
uint32_t newUid = b.newUniqueId();
auto& c = GetZ3Context();
auto predicate = c.bool_const( format( "b{}", bbIndex ).c_str() );
if( oldUid == 0 )
{
auto apply = buildFunctionApplication( b, newUid, gfDecl, gfa );
b.add( z3::implies( predicate, apply == Coerce( expr, { apply, gfDecl.returnType,
gfa.locationId() } ) ) );
}
else
{
// Not the first time we set a value for this ghost func:
// we need to express that its new value at the current assignation point is the new one only if
// the args are equal to the passed args, otherwise it is equal to whatever it was at its previous
// assignment.
auto argCount = gfa.args().size();
llvm::SmallVector< z3::expr, 8 > z3args;
z3args.reserve( argCount + 1 );
z3args.emplace_back( c.int_val( newUid ) );
uint32_t argIndex = 0;
z3::expr_vector z3argComparisons( c );
z3argComparisons.resize( argCount );
z3::expr_vector z3argsVars( c );
z3argsVars.resize( argCount );
for( auto&& arg : gfa.args() )
{
auto argConst = BuildZ3ConstantFromType( b, arg.type, format( "gca{}_{}", argIndex, newUid ) ).expr;
z3args.emplace_back( argConst );
z3argsVars.set( argIndex, argConst );
auto comp = argConst == arg.expr;
z3argComparisons.set( argIndex, comp );
++argIndex;
}
auto newApply = gfDecl.z3decl( z3args.size(), z3args.data() );
z3args[0] = c.int_val( oldUid );
auto previousApply = gfDecl.z3decl( z3args.size(), z3args.data() );
// AAAAAAAH
b.add( z3::forall( z3argsVars, z3::implies( predicate, newApply == z3::ite(
z3::mk_and( z3argComparisons ),
Coerce( expr, { newApply, gfDecl.returnType, gfa.locationId() } ),
previousApply
) ) ) );
}
setCurrentUidForBasicBlock( bbIndex, gfIndex, newUid );
}
optional< Z3Val > GFCTracker::retrieve( Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa )
{
if( bbIndex == b.currentBBIndex() )
return nullopt;
if( bbIndex == ~0U )
bbIndex = b.currentBBIndex();
auto gfDecl = GhostFuncDeclCache::GetInstance()->getDecl( b.context(), gfa.func() );
if( !gfDecl )
return nullopt;
auto gft = *FromValue< builtins::FuncType >( *EIRToValue( gfa.func().type() ) );
uint32_t gfIndex = getGFIndex( gfa );
auto uid = getCurrentUidForBasicBlock( bbIndex, gfIndex );
if( uid )
return Z3Val{ buildFunctionApplication( b, uid, *gfDecl, gfa ), *EIRToValue( gft.returnType() ),
gfa.locationId() };
if( !b.cfg() )
return nullopt;
uid = b.newUniqueId();
auto& c = GetZ3Context();
optional< z3::expr > lhsExpr;
llvm::SmallVector< z3::expr, 8 > z3args;
z3::expr_vector z3argsVars( c );
optional< z3::expr > newApply;
// Model the ssa phi operation by creating a series of equality assertions, each implied by
// one of the possible incoming edge condition for the current basic block.
if( b.remapper() )
{
b.remapper()->forEachIncomingEdge( bbIndex, [&]( auto&& predIndex, auto&& expr )
{
if( auto predVal = retrieve( b, predIndex, gfa ) )
{
if( !lhsExpr )
lhsExpr = buildFunctionApplication( b, uid, *gfDecl, gfa );
if( z3args.empty() )
{
auto argCount = gfa.args().size();
z3args.reserve( argCount + 1 );
z3args.emplace_back( c.int_val( uid ) );
z3argsVars.resize( argCount );
uint32_t argIndex = 0;
for( auto&& arg : gfa.args() )
{
auto argConst = BuildZ3ConstantFromType( b, arg.type, format( "gca{}_{}", argIndex, uid ) ).expr;
z3args.emplace_back( argConst );
z3argsVars.set( argIndex, argConst );
++argIndex;
}
newApply = gfDecl->z3decl( z3args.size(), z3args.data() );
}
auto predUid = getCurrentUidForBasicBlock( predIndex, gfIndex );
z3args[0] = c.int_val( predUid );
auto previousApply = gfDecl->z3decl( z3args.size(), z3args.data() );
b.add( z3::forall( z3argsVars, z3::implies( c.bool_const( format( "e{}_{}", predIndex, bbIndex ).c_str() ),
*newApply == previousApply ) ) );
}
} );
}
if( !lhsExpr )
return nullopt;
setCurrentUidForBasicBlock( bbIndex, gfIndex, uid );
setForBasicBlock( b, bbIndex, gfa, gfIndex, *gfDecl, move( *lhsExpr ) );
return Z3Val{ buildFunctionApplication( b, uid, *gfDecl, gfa ), *EIRToValue( gft.returnType() ),
gfa.locationId() };
}
void GFCTracker::set( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val )
{
auto gfDecl = GhostFuncDeclCache::GetInstance()->getDecl( b.context(), gfa.func() );
if( !gfDecl )
return;
uint32_t gfIndex = getGFIndex( gfa );
return setForBasicBlock( b, b.currentBBIndex(), gfa, gfIndex, *gfDecl, move( val.expr ) );
}
|
Changes to bs/verify/gfctracker.h.
1 2 3 4 5 6 7 8 9 10 |
#ifndef GOOSE_VERIFY_GFCTRACKER_H
#define GOOSE_VERIFY_GFCTRACKER_H
namespace goose::verify
{
class Builder;
class GFCTracker
{
public:
| > > > > | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#ifndef GOOSE_VERIFY_GFCTRACKER_H
#define GOOSE_VERIFY_GFCTRACKER_H
namespace goose::verify
{
class Builder;
class GFCTracker
{
public:
GFCTracker( size_t size ) :
m_gfcStorage( size )
{}
optional< Z3Val > retrieve( Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa );
void set( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val );
private:
uint32_t getGFIndex( const GhostFuncApplication& gfa );
z3::expr buildFunctionApplication( Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const Value& gf );
z3::expr buildFunctionApplication( Builder& b, uint32_t uid, const GhostFuncDeclCache::GFDecl& gfDecl, const GhostFuncApplication& gfa );
uint32_t getCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex ) const;
void setCurrentUidForBasicBlock( uint32_t bbIndex, uint32_t gfIndex, uint32_t uid );
void setForBasicBlock( Builder& b, uint32_t bbIndex, const GhostFuncApplication& gfa,
uint32_t gfIndex, const GhostFuncDeclCache::GFDecl& gfDecl, z3::expr&& expr );
// Each ghost func have a unique index within the current function.
// (0: invalid index)
unordered_map< Value, uint32_t > m_ghostFuncIndices;
// For each ghostfunc closure, store its last index.
|
| ︙ | ︙ |
Changes to bs/verify/ghostfunc.h.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 |
optional< GFDecl > getDecl( const sema::Context& c, const Value& gf );
private:
optional< GFDecl > createDecl( const sema::Context& c, const Value& gf );
unordered_map< Value, GFDecl > m_decls;
};
}
#endif
| > > > > > > > > > > > > > > > > > > > > > > > > > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
optional< GFDecl > getDecl( const sema::Context& c, const Value& gf );
private:
optional< GFDecl > createDecl( const sema::Context& c, const Value& gf );
unordered_map< Value, GFDecl > m_decls;
};
class GhostFuncApplication
{
public:
GhostFuncApplication( const cir::Value& func, LocationId loc ) :
m_func( func ),
m_loc( loc )
{}
GhostFuncApplication( cir::Value&& func, LocationId loc ) :
m_func( move( func ) ),
m_loc( loc )
{}
const auto& func() const { return m_func; }
const auto& args() const { return m_args; }
auto& args() { return m_args; }
auto locationId() const { return m_loc; }
private:
eir::Value m_func;
llvm::SmallVector< Z3Val, 4 > m_args;
LocationId m_loc;
};
}
#endif
|
Changes to bs/verify/helpers.inl.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 |
auto& tp = **optTypePreds;
const auto* prevValPH = b.retrievePlaceholder( "@val"_sid );
b.setPlaceholder( "@val"_sid, valExpr );
tp.forEach( [&]( auto&& p )
{
| | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
auto& tp = **optTypePreds;
const auto* prevValPH = b.retrievePlaceholder( "@val"_sid );
b.setPlaceholder( "@val"_sid, valExpr );
tp.forEach( [&]( auto&& p )
{
auto zv = BuildZ3ExprFromValue( b, p );
func( zv.expr, p.locationId() );
return true;
} );
if( prevValPH )
b.setPlaceholder( "@val"_sid, *prevValPH );
else
|
| ︙ | ︙ |
Added bs/verify/instrseq.cpp.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
#include "verify.h"
#include "builtins/builtins.h"
#include "diagnostics/diagnostics.h"
#include "helpers.inl"
using namespace goose;
using namespace goose::verify;
using namespace goose::diagnostics;
namespace
{
bool CheckAssertion( z3::solver& solver, const z3::expr& expr, const z3::expr& exprToCheck, LocationId locationId )
{
if( verify::Func::TraceMode() )
return true;
ProfileZoneScoped;
z3::expr_vector exprVec( GetZ3Context() );
exprVec.push_back( exprToCheck );
solver.push();
bool result = false;
switch( solver.check( exprVec ) )
{
case z3::check_result::unsat:
// We are using the solver to try and find a counter example,
// so if it's unsat, it's good.
if( verify::Func::DumpSolverOnSuccess() )
cout << "Solver dump:\n" << solver << "check_unsat: " << exprToCheck << endl << endl;
result = true;
break;
case z3::check_result::sat:
DiagnosticsManager::GetInstance().emitErrorMessage( locationId, "assert"_sid,
"this condition may not be met." );
if( verify::Func::DumpSolverOnFailure() )
{
cout << "Solver dump:\n" << solver << "check_unsat: " << exprToCheck << endl << endl;
cout << "Model dump:\n" << solver.get_model() << endl << endl;
}
result = false;
break;
default:
DiagnosticsManager::GetInstance().emitErrorMessage( locationId, "assert"_sid,
"couldn't verify this condition." );
if( verify::Func::DumpSolverOnFailure() )
cout << "Solver dump:\n" << solver << "check_unsat: " << exprToCheck << endl << endl;
result = false;
break;
}
solver.pop();
return result;
}
}
namespace goose::verify
{
bool VerifyInstrSeq( const sema::Context& c, const InstrSeq& is )
{
z3::solver solver( GetZ3Context(), z3::solver::simple() );
Builder b( c, solver, 0 );
if( Func::TraceMode() )
{
cout << "=== Checking instr seq ===\n";
b.setTraceMode( true );
}
b.setAssertionHandler( [&]( auto&& expr, auto&& exprToCheck, LocationId locationId )
{
return CheckAssertion( solver, expr, exprToCheck, locationId );
} );
return BuildZ3Op( b, is ) && !b.hasCheckFailed();
}
}
|
Changes to bs/verify/loop.cpp.
| ︙ | ︙ | |||
75 76 77 78 79 80 81 |
// Havoc all the addresses modified during the current loop and emit
// the first unrolling for the induction case.
m_remapper.nextLoopIteration();
uint32_t firstInductionIterationHeaderIndex = m_remapper.remapBBId( header );
m_builder.setCurrentBB( firstInductionIterationHeaderIndex );
if( ms_TraceMode )
| | | | | 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
// Havoc all the addresses modified during the current loop and emit
// the first unrolling for the induction case.
m_remapper.nextLoopIteration();
uint32_t firstInductionIterationHeaderIndex = m_remapper.remapBBId( header );
m_builder.setCurrentBB( firstInductionIterationHeaderIndex );
if( ms_TraceMode )
cout << " == Havocing modified storage locations for loop " << header.index() << endl;
m_cfg->forEachStorageLocationModifiedByLoop( header.index(), [&]( auto&& type, auto&& sloc )
{
HavocStorageLocation( m_builder, firstInductionIterationHeaderIndex, type, sloc );
} );
if( ms_TraceMode )
cout << " == Unrolling first induction case for loop " << header.index() << endl;
buildZ3Expressions( header, &parentWorkQueue );
|
| ︙ | ︙ |
Changes to bs/verify/meson.build.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
goose_verify = library( 'goose-verify',
'verify.cpp',
'type.cpp',
'value.cpp',
'storage.cpp',
'phi.cpp',
'call.cpp',
'builder.cpp',
'remapper.cpp',
'vartracker.cpp',
'gfctracker.cpp',
'propositions.cpp',
'func.cpp',
'ghostfunc.cpp',
'cfg.cpp',
'loop.cpp',
'basicblock.cpp',
'terminator.cpp',
| > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
goose_verify = library( 'goose-verify',
'verify.cpp',
'type.cpp',
'value.cpp',
'storage.cpp',
'phi.cpp',
'call.cpp',
'builder.cpp',
'stack.cpp',
'remapper.cpp',
'vartracker.cpp',
'gfctracker.cpp',
'propositions.cpp',
'func.cpp',
'ghostfunc.cpp',
'cfg.cpp',
'loop.cpp',
'basicblock.cpp',
'terminator.cpp',
'instrseq.cpp',
'verifyviz.cpp',
include_directories: bsinc,
dependencies: [z3_dep, tracy_dep]
)
|
Changes to bs/verify/phi.cpp.
1 2 3 4 5 |
#include "verify.h"
#include "builtins/builtins.h"
namespace goose::verify
{
| | | < < < < < | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#include "verify.h"
#include "builtins/builtins.h"
namespace goose::verify
{
bool BuildZ3Op( Builder& b, const Phi& instr )
{
const auto* remapper = b.remapper();
if( !remapper )
return false;
auto newVar = BuildZ3ConstantFromType( b, instr.type(), format( "v{}", b.newUniqueId() ) );
auto& c = GetZ3Context();
uint32_t bbIndex = b.currentBBIndex();
// Model the ssa phi operation by creating a series of equality assertions, each implied by
// one of the possible incoming edge condition for the current basic block.
remapper->forEachIncomingEdge( bbIndex, [&]( auto&& predIndex, auto&& predExpr )
{
uint32_t origPredIndex = remapper->getOriginalBBIndex( predIndex );
// Match the incoming edge with the correct one in the phi instruction.
instr.forAllIncomings( [&]( auto&& bb, auto&& val )
{
if( bb->index() != origPredIndex )
return true;
auto zv = BuildZ3ExprFromValue( b, val );
b.add( z3::implies( c.bool_const( format( "e{}_{}", predIndex, bbIndex ).c_str() ),
newVar.expr == Coerce( zv, newVar ) ) );
return true;
} );
} );
b.setVar( instr.destIndex(), move( newVar ) );
return true;
}
}
|
Changes to bs/verify/propositions.cpp.
1 2 3 4 5 6 7 8 9 10 |
#include "verify.h"
#include "diagnostics/diagnostics.h"
#include "builtins/builtins.h"
using namespace goose::verify;
using namespace goose::diagnostics;
Propositions::Propositions( const sema::Context& c ) :
m_context( c ),
m_solver( GetZ3Context() ),
| | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#include "verify.h"
#include "diagnostics/diagnostics.h"
#include "builtins/builtins.h"
using namespace goose::verify;
using namespace goose::diagnostics;
Propositions::Propositions( const sema::Context& c ) :
m_context( c ),
m_solver( GetZ3Context() ),
m_builder( m_context, m_solver, 0 )
{}
bool Propositions::addPropositions( const ValueVec& assList )
{
for( auto&& val : assList )
{
if( val.isPoison() )
return false;
if( val.type() != GetValueType< bool >() )
continue;
auto zv = TryBuildZ3ExprFromValue( m_builder, val );
if( !zv )
{
DiagnosticsManager::GetInstance().emitErrorMessage( val.locationId(),
"invalid proposition." );
return false;
}
|
| ︙ | ︙ |
Added bs/verify/stack.cpp.
> > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#include "verify.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::builtins;
namespace goose::verify
{
optional< Z3Val > Stack::PopAsZ3Val( Builder& b )
{
auto result = visit( [&]< typename T >( const T& elem ) -> optional< Z3Val >
{
if constexpr( is_same_v< T, Value > )
return BuildZ3ExprFromValue( b, elem );
else if constexpr( is_same_v< T, Address > )
return BuildAddressExpr( elem );
else
return nullopt;
}, m_stack.top() );
if( result )
m_stack.pop();
return result;
}
}
|
Added bs/verify/stack.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
#ifndef GOOSE_VERIFY_STACK_H
#define GOOSE_VERIFY_STACK_H
namespace goose::verify
{
class Stack
{
public:
template< typename T >
void push( T&& x )
{
m_stack.push( forward< T >( x ) );
}
template< typename T >
optional< T > pop( Builder& b )
{
if( m_stack.empty() )
return nullopt;
if( !holds_alternative< T >( m_stack.top() ) )
{
if constexpr( is_same_v< T, Z3Val > )
return PopAsZ3Val( b );
else
return nullopt;
}
optional< T > result = move( get< T >( m_stack.top() ) );
m_stack.pop();
return result;
}
private:
optional< Z3Val > PopAsZ3Val( Builder& b );
using Slot = variant< Value, Z3Val, Address, GhostFuncApplication >;
stack< Slot > m_stack;
};
}
#endif
|
Changes to bs/verify/storage.cpp.
1 2 3 4 5 6 7 8 |
#include "verify.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::builtins;
namespace goose::verify
{
| > > > > | | > > > | | > | > | | > | > > > | < | < < | | | | < | < > | > | | > > | > > > > | < | | < | > | | | > > > | > > > | | | | | > | | | | > > > | | < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
#include "verify.h"
#include "builtins/builtins.h"
using namespace goose;
using namespace goose::builtins;
namespace goose::verify
{
optional< Z3Val > LoadVar( Builder& b, uint32_t index )
{
return b.retrieveVar( index );
}
optional< Z3Val > LoadVar( Builder& b, uint32_t index, const Term& type )
{
auto result = b.retrieveVar( index );
if( !result )
result = BuildZ3ConstantFromType( b, type, format( "var{}", index ) );
return result;
}
optional< Z3Val > LoadFromBaseAddress( Builder& b, const Address& addr )
{
optional< Z3Val > val;
switch( addr.origin() )
{
case Address::Origin::Stack:
val = LoadVar( b, addr.path().front() );
break;
case Address::Origin::Heap:
// TODO
break;
}
return val;
}
optional< Z3Val > LoadFromBaseAddress( Builder& b, const Address& addr, const Term& type )
{
optional< Z3Val > val;
switch( addr.origin() )
{
case Address::Origin::Stack:
val = LoadVar( b, addr.path().front(), type );
break;
case Address::Origin::Heap:
// TODO
break;
}
return val;
}
optional< Z3Val > LoadFromAddress( Builder& b, const Address& addr, const Term& type )
{
optional< Z3Val > val;
if( addr.path().size() == 1 )
val = LoadFromBaseAddress( b, addr, type );
else
val = LoadFromBaseAddress( b, addr );
if( !val )
return nullopt;
for( uint32_t i = 1; i < addr.path().size(); ++i )
{
auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), ValueToEIR( val->type ) );
if( !tinfo )
return nullopt;
// The only aggregate type that we handle for now are tuples.
// TODO: arrays
auto memIndex = addr.path()[i];
auto elemType = GetTupleTypeElement( val->type, memIndex );
auto elemExpr = tinfo->proj( val->expr, memIndex );
val = Z3Val{ move( elemExpr ), *EIRToValue( elemType ), addr.locationId() };
}
return val;
}
optional< Z3Val > LoadFromAddress( Builder& b, const GhostFuncApplication& gfa )
{
return b.retrieveGFC( gfa );
}
optional< z3::expr > ModifyAggregate( Builder& b, const Z3Val& aggregate, const SelectPath& path, uint32_t index, Z3Val&& valToStore )
{
auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), ValueToEIR( aggregate.type ) );
if( !tinfo )
return nullopt;
// The only aggregate type that we handle for now are tuples.
|
| ︙ | ︙ | |||
88 89 90 91 92 93 94 |
// Copy all elements from the existing value as is,
// except for the one pointed to by the current path index.
if( i == elemIndex )
{
// If we didn't reach the end of the path yet, recurse.
// Otherwise, it means we finally reached the nested member that we wanted to modify,
// so push the new value.
| | | > < | < | < < | | | < | < | < > > > | | < | < < < | < < | < < | < | | < < | < | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | < | > > > | > > | 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
// Copy all elements from the existing value as is,
// except for the one pointed to by the current path index.
if( i == elemIndex )
{
// If we didn't reach the end of the path yet, recurse.
// Otherwise, it means we finally reached the nested member that we wanted to modify,
// so push the new value.
if( --index > 0 )
{
auto newElem = ModifyAggregate( b, Z3Val{ move( elemExpr ), *EIRToValue( elemType ), aggregate.loc },
path, index, move( valToStore ) );
if( !newElem )
return nullopt;
args.push_back( move( *newElem ) );
}
else
args.push_back( valToStore.expr );
}
else
args.push_back( move( elemExpr ) );
}
return tinfo->ctor( args );
}
void StoreToBaseAddress( Builder& b, const Address& addr, Z3Val&& val )
{
switch( addr.origin() )
{
case Address::Origin::Stack:
b.setVar( addr.path().front(), move( val ) );
break;
case Address::Origin::Heap:
// TODO
break;
}
}
void StoreToAddress( Builder& b, const Address& addr, Z3Val&& val )
{
if( addr.path().size() == 1 )
{
StoreToBaseAddress( b, addr, move( val ) );
return;
}
// If the address contains a path (the result of a series of Select cir instructions),
// it means we are storing into a member (of a member) of an aggregate type.
// In this case, we need to load the existing value of the aggregate,
// rebuild it with the modified member, and store it.
auto aggregate = LoadFromBaseAddress( b, addr );
if( !aggregate )
return;
if( auto newAggregateToStore = ModifyAggregate( b, *aggregate, addr.path(), 1, move( val ) ) )
StoreToBaseAddress( b, addr, Z3Val{ *newAggregateToStore, aggregate->type, addr.locationId() } );
}
void StoreToAddress( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val )
{
b.setGFC( gfa, move( val ) );
}
Z3Val BuildAddressExpr( const Address& addr )
{
auto& zc = GetZ3Context();
z3::expr_vector zvec( zc );
zvec.resize( addr.path().size() );
// TODO handle non-stack origin
uint32_t i = 0;
for( auto&& index : addr.path() )
{
auto zindex = zc.int_val( index ).unit();
zvec.set( i++, zindex );
}
return
{
z3::concat( zvec ),
*EIRToValue( GetValueType< builtins::MemLoc >() ),
addr.locationId()
};
}
optional< verify::GhostFuncApplication > CIRGFAToVerifyGFA( Builder& b, const cir::GhostFuncApplication& cgfa )
{
verify::GhostFuncApplication vgfa( cgfa.func(), cgfa.locationId() );
vgfa.args().reserve( cgfa.args().size() );
for( auto&& a : cgfa.args() )
{
visit( [&]< typename T >( const T& x )
{
if constexpr( is_same_v< T, eir::Value > )
{
vgfa.args().emplace_back( BuildZ3ExprFromValue( b, x ) );
}
else if constexpr( is_same_v< T, Address > )
{
vgfa.args().emplace_back( BuildAddressExpr( x ) );
}
}, a );
}
if( vgfa.args().size() != cgfa.args().size() )
return nullopt;
return vgfa;
}
void HavocStorageLocation( Builder& b, uint32_t bbIndex, const Term& type, const StorageLocation& sloc )
{
auto valToStore = BuildZ3ConstantFromType( b, type, format( "v{}", b.newUniqueId() ) );
visit( [&]< typename T >( const T& x )
{
if constexpr( is_same_v< T, Address > )
{
StoreToAddress( b, x, move( valToStore ) );
}
else if constexpr( is_same_v< T, cir::GhostFuncApplication >)
{
if( auto vgfa = CIRGFAToVerifyGFA( b, x ) )
StoreToAddress( b, *vgfa, move( valToStore ) );
}
}, sloc );
}
}
|
Changes to bs/verify/storage.h.
1 2 3 4 5 |
#ifndef GOOSE_VERIFY_STORAGE_H
#define GOOSE_VERIFY_STORAGE_H
namespace goose::verify
{
| > > > > > | > > > > | | | | < | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#ifndef GOOSE_VERIFY_STORAGE_H
#define GOOSE_VERIFY_STORAGE_H
namespace goose::verify
{
// Currently, we only synthethize z3 formulas for load/stores to constant addresses.
// Stores to symbolic addresses are ignored, loads from symbolic addresses return
// an unknown variable of the requisite type.
//
// Therefore the verifier can't currently reason about a possible aliasing between
// a symbolic address and another address.
//
// This should be addressed (...) at higher level by assuming that two addresses tagged
// with the same lifetime will always alias (ie modifiyng the pointeed from one of them
// invalidates every other pointee with the same lifetime).
extern optional< Z3Val > LoadVar( Builder& b, uint32_t index );
extern optional< Z3Val > LoadVar( Builder& b, uint32_t index, const Term& type );
extern optional< Z3Val > LoadFromAddress( Builder& b, const Address& addr, const Term& type );
extern optional< Z3Val > LoadFromAddress( Builder& b, const GhostFuncApplication& gfa );
extern void StoreToAddress( Builder& b, const Address& addr, Z3Val&& val );
extern void StoreToAddress( Builder& b, const GhostFuncApplication& gfa, Z3Val&& val );
extern Z3Val BuildAddressExpr( const Address& addr );
extern void HavocStorageLocation( Builder& b, uint32_t bbIndex, const Term& type, const StorageLocation& sloc );
}
#endif
|
Changes to bs/verify/terminator.cpp.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
bool Func::handleTerminator( uint32_t bbIndex, const cir::Terminator& t )
{
return visit( [&]( auto&& t )
{
return handleTerminator( bbIndex, t );
}, t.content() );
}
bool Func::handleTerminator( uint32_t bbIndex, const cir::Ret& tr )
{
// Emit the "ensures" expressions as assertions.
if( !m_func )
return true;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < | | | | | | | < | | < | < < < < < < < < | < < < < | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
bool Func::handleTerminator( uint32_t bbIndex, const cir::Terminator& t )
{
return visit( [&]( auto&& t )
{
return handleTerminator( bbIndex, t );
}, t.content() );
}
bool Func::handleTerminator( uint32_t bbIndex, const cir::RetVoid& tr )
{
// Emit the "ensures" expressions as assertions.
if( !m_func )
return true;
DiagnosticsContext dc( tr.locationId(), "When returning here." );
Builder cb = m_builder;
const auto& postConds = m_func->type().verifInfos()->postConds();
bool success = true;
postConds->forEach( [&]( auto&& cond )
{
// Don't do any error handling here, it should already have been taken care of
// by the condition verifier.
if( auto zv = TryBuildZ3ExprFromValue( cb, cond ) )
{
bool succ = m_builder.checkAssertion( zv->expr, cond.locationId() );
success = success && succ;
}
} );
return success;
}
bool Func::handleTerminator( uint32_t bbIndex, const cir::Ret& tr )
{
auto retExpr = m_builder.pop();
// Emit the "ensures" expressions as assertions.
if( !m_func )
return true;
DiagnosticsContext dc( retExpr->loc, "When returning this." );
Builder cb = m_builder;
if( retExpr )
{
// Set the @result placeholder for the func's post conditions
cb.setPlaceholder( "@result"_sid, retExpr->expr );
// Emit the return type's predicates as assertions.
ForEachPredicate( cb, m_retType, retExpr->expr, [&]( auto&& z3expr, auto locId )
{
m_builder.checkAssertion( z3expr, locId );
} );
}
const auto& postConds = m_func->type().verifInfos()->postConds();
bool success = true;
postConds->forEach( [&]( auto&& cond )
{
// Don't do any error handling here, it should already have been taken care of
// by the condition verifier.
if( auto zv = TryBuildZ3ExprFromValue( cb, cond ) )
{
bool succ = m_builder.checkAssertion( zv->expr, cond.locationId() );
success = success && succ;
}
} );
return success;
}
|
| ︙ | ︙ | |||
95 96 97 98 99 100 101 |
uint32_t srcBBId = m_remapper.remapBBId( *bb );
uint32_t trueBBId = m_remapper.remapOutgoingEdge( *bb, *pTrueDestBB );
uint32_t falseBBId = m_remapper.remapOutgoingEdge( *bb, *pFalseDestBB );
m_viz.addEdge( srcBBId, trueBBId );
m_viz.addEdge( srcBBId, falseBBId );
| | | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
uint32_t srcBBId = m_remapper.remapBBId( *bb );
uint32_t trueBBId = m_remapper.remapOutgoingEdge( *bb, *pTrueDestBB );
uint32_t falseBBId = m_remapper.remapOutgoingEdge( *bb, *pFalseDestBB );
m_viz.addEdge( srcBBId, trueBBId );
m_viz.addEdge( srcBBId, falseBBId );
if( auto cond = m_builder.pop() )
{
m_remapper.addEdge( srcBBId, trueBBId, cond->expr );
m_remapper.addEdge( srcBBId, falseBBId, !cond->expr );
return true;
}
// In order to fail gracefully in case we couldn't convert the condition,
|
| ︙ | ︙ |
Changes to bs/verify/type.cpp.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
TypeCache* TypeCache::GetInstance()
{
static unique_ptr< TypeCache > tcache( new TypeCache() );
return tcache.get();
}
TypeCache::TypeCache() :
| | > > | > > > > > > > > > > > > > > > > > | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
TypeCache* TypeCache::GetInstance()
{
static unique_ptr< TypeCache > tcache( new TypeCache() );
return tcache.get();
}
TypeCache::TypeCache() :
m_memLocSort( GetZ3Context() ),
m_uninterpretedSort( GetZ3Context() )
{
auto s = GetZ3Context().int_sort();
m_memLocSort = GetZ3Context().seq_sort( s );
m_uninterpretedSort = GetZ3Context().uninterpreted_sort( "goose_uninterpreted_sort" );
}
optional< TypeInfo > TypeCache::getTypeInfo( const sema::Context& c, const Term& type )
{
auto it = m_typeInfos.find( type );
if( it != m_typeInfos.end() )
return it->second;
auto tinfo = createTypeInfo( c, type );
if( !tinfo )
return nullopt;
m_typeInfos.emplace( type, *tinfo );
return tinfo;
}
const TypeInfo& TypeCache::getUninterpretedTypeInfo() const
{
static TypeInfo UnintTypeInfo { m_uninterpretedSort,
[&]( auto&& b )
{
return GetZ3Context().constant( format( "uc{}", b.newUniqueId() ).c_str(), m_uninterpretedSort );
},
[&]( auto&& b, auto&& val )
{
return GetZ3Context().constant( format( "uv{}", b.newUniqueId() ).c_str(), m_uninterpretedSort );
},
{}, {}
};
return UnintTypeInfo;
}
optional< TypeInfo > TypeCache::createTypeInfo( const Value& typeVal )
{
// Handle all non-aggregate types (bool, signed int, unsigned int, floats) directly.
// TODO: some are missing and will be dealt with later on.
if( ValueToEIR( typeVal ) == GetValueType< bool >() )
{
return TypeInfo { GetZ3Context().bool_sort(),
[]( auto&& b )
|
| ︙ | ︙ | |||
116 117 118 119 120 121 122 |
return TypeInfo { m_memLocSort,
[&]( auto&& b )
{
return GetZ3Context().constant( format( "ml{}", b.newUniqueId() ).c_str(), m_memLocSort );
},
[&]( auto&& b, auto&& val )
{
| | | | | | | | 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
return TypeInfo { m_memLocSort,
[&]( auto&& b )
{
return GetZ3Context().constant( format( "ml{}", b.newUniqueId() ).c_str(), m_memLocSort );
},
[&]( auto&& b, auto&& val )
{
if( !BuildZ3Op( b, *val.cir() ) )
return GetZ3Context().constant( format( "ml{}", b.newUniqueId() ).c_str(), m_memLocSort );
return b.pop()->expr;
},
{}, {}
};
}
if( ValueToEIR( typeVal ) == GetValueType< MemLoc >() )
{
return TypeInfo { m_memLocSort,
[&]( auto&& b )
{
return GetZ3Context().constant( format( "ml{}", b.newUniqueId() ).c_str(), m_memLocSort );
},
{}, // TODO val (not needed for now)
{}, {}
};
}
return nullopt;
}
optional< TypeInfo > TypeCache::createTypeInfo( const sema::Context& c, const Term& type )
{
auto typeVal = *EIRToValue( type );
auto tinfo = createTypeInfo( typeVal );
if( tinfo )
return tinfo;
// The only non aggregate type that we handle at the moment are tuples.
// Other higher level types such as structs and classes need to be lowered
// to tuples by a suitable overload of LowerTypeForVerification.
// TODO: Later we'll also need to handle additional types such as arrays
// and strings here.
auto loweredType = LowerTypeForVerification( c, typeVal );
if( !loweredType || loweredType->isPoison() )
return nullopt;
if( !IsTupleType( *loweredType ) )
return createTypeInfo( *loweredType );
auto sortName = format( "tupsort{}", ms_nextUniqueId++ );
auto size = TupleTypeSize( *loweredType );
llvm::SmallVector< TypeInfo, 8 > elemTInfos;
llvm::SmallVector< string, 8 > elemNames;
|
| ︙ | ︙ |
Changes to bs/verify/type.h.
| ︙ | ︙ | |||
16 17 18 19 20 21 22 23 24 25 26 |
// (for instances tuples), we keep a cache to avoid recreating them multiple times.
class TypeCache
{
public:
static TypeCache* GetInstance();
optional< TypeInfo > getTypeInfo( const sema::Context& c, const Term& type );
private:
TypeCache();
| > | | > | 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
// (for instances tuples), we keep a cache to avoid recreating them multiple times.
class TypeCache
{
public:
static TypeCache* GetInstance();
optional< TypeInfo > getTypeInfo( const sema::Context& c, const Term& type );
const TypeInfo& getUninterpretedTypeInfo() const;
private:
TypeCache();
optional< TypeInfo > createTypeInfo( const Value& typeVal );
optional< TypeInfo > createTypeInfo( const sema::Context& c, const Term& type );
unordered_map< Term, TypeInfo > m_typeInfos;
z3::sort m_memLocSort;
z3::sort m_uninterpretedSort;
static uint32_t ms_nextUniqueId;
};
}
#endif
|
Deleted bs/verify/valtracker.inl.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < |
Changes to bs/verify/value.cpp.
1 2 3 4 5 6 7 8 |
#include "verify.h"
#include "builtins/builtins.h"
#include "helpers.inl"
using namespace goose::diagnostics;
namespace goose::verify
{
| | > > > > > > > > > > > > > > > | | > > > > > > > > > > | | > > > > > > > > > > > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
#include "verify.h"
#include "builtins/builtins.h"
#include "helpers.inl"
using namespace goose::diagnostics;
namespace goose::verify
{
Z3Val BuildZ3ValFromConstant( Builder& b, const Value& val )
{
optional< TypeInfo > tinfo;
auto loweredVal = LowerConstantForVerification( b.context(), val );
if( loweredVal && !loweredVal->isPoison() )
tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), loweredVal->type() );
if( !tinfo )
tinfo = TypeCache::GetInstance()->getUninterpretedTypeInfo();
auto zexpr = tinfo->build( b, *loweredVal );
return Z3Val { move( zexpr ), *EIRToValue( val.type() ), val.locationId() };
}
optional< Z3Val > TryBuildZ3ValFromConstant( Builder& b, const Value& val )
{
auto loweredVal = LowerConstantForVerification( b.context(), val );
if( !loweredVal || loweredVal->isPoison() )
return nullopt;
auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), loweredVal->type() );
if( !tinfo )
return nullopt;
auto zexpr = tinfo->build( b, *loweredVal );
return Z3Val { move( zexpr ), *EIRToValue( val.type() ), val.locationId() };
}
Z3Val BuildZ3ConstantFromType( Builder& b, const Value& type, const string& name )
{
auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), ValueToEIR( type ) );
if( !tinfo )
tinfo = TypeCache::GetInstance()->getUninterpretedTypeInfo();
assert( tinfo->sort );
return Z3Val { GetZ3Context().constant( name.c_str(), *tinfo->sort ), type, type.locationId() };
}
optional< Z3Val > TryBuildZ3ConstantFromType( Builder& b, const Value& type, const string& name )
{
auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), ValueToEIR( type ) );
if( !tinfo )
return nullopt;
assert( tinfo->sort );
return Z3Val { GetZ3Context().constant( name.c_str(), *tinfo->sort ), type, type.locationId() };
}
Z3Val BuildZ3ConstantFromType( Builder& b, const Term& type, const string& name )
{
auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), type );
if( !tinfo )
tinfo = TypeCache::GetInstance()->getUninterpretedTypeInfo();
assert( tinfo->sort );
auto tval = *EIRToValue( type );
return Z3Val { GetZ3Context().constant( name.c_str(), *tinfo->sort ), tval, tval.locationId() };
}
optional< Z3Val > TryBuildZ3ConstantFromType( Builder& b, const Term& type, const string& name )
{
auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), type );
if( !tinfo )
return nullopt;
assert( tinfo->sort );
auto tval = *EIRToValue( type );
return Z3Val { GetZ3Context().constant( name.c_str(), *tinfo->sort ), tval, tval.locationId() };
}
z3::expr GetAsBitVec( const z3::expr& expr, const Value& type )
{
if( expr.is_bv() )
return expr;
|
| ︙ | ︙ | |||
88 89 90 91 92 93 94 |
else if( to.expr.is_bv() )
return GetAsBitVec( expr, to.type );
else
return expr;
}
template< typename I, typename F >
| | | | | > | | | | | | | > | > > | > | > | | > > > > > > > > | | | | | | < | < | < < > | | < < | > | > | | | | | | | | > | > > | > | > | | | | | | | | | | | > > > > > > > > > > > > | | | > > > > > | > > > | < < | | < | > > > > > | | | | | | > > > | > > > | | | > > > > | | | > > | | > | > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | > > | | | > | > | > | > > | | > > < < | | < | | > | < < < | < | | > > > > > | > | > > | < < > | > > | | > | < < | > > > > > | > | | | | | > | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > | | | | | 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 |
else if( to.expr.is_bv() )
return GetAsBitVec( expr, to.type );
else
return expr;
}
template< typename I, typename F >
bool BuildZ3UnaryExpr( Builder& b, const I& instr, F&& func )
{
auto operand = b.pop();
if( !operand )
return false;
b.push( Z3Val{ func( operand->expr ), operand->type, instr.locationId() } );
return true;
}
template< typename I, typename F >
bool BuildZ3BinExpr( Builder& b, const I& instr, F&& func )
{
auto rhs = b.pop();
if( !rhs )
return false;
auto lhs = b.pop();
if( !lhs )
return false;
if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )
{
b.push( Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), lhs->type, instr.locationId() } );
return true;
}
// If we are trying to do an operation on a mix of bitvec and int,
// convert the int to a bitvec first.
if( lhs->expr.is_bv() )
{
assert( rhs->expr.is_int() );
b.push( Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), lhs->type, instr.locationId() } );
return true;
}
else
{
assert( lhs->expr.is_int() );
b.push( Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), lhs->type, instr.locationId() } );
return true;
}
return false;
}
template< typename I, typename F >
bool BuildZ3BinBitwiseExpr( Builder& b, const I& instr, F&& func )
{
auto rhs = b.pop();
if( !rhs )
return false;
auto lhs = b.pop();
if( !lhs )
return false;
if( ValueToEIR( lhs->type ) == GetValueType< BigInt >() )
{
DiagnosticsManager::GetInstance().emitErrorMessage( 0, "verifier error: bitwise operands can't be ct_int." );
return false;
}
if( ValueToEIR( rhs->type ) == GetValueType< BigInt >() )
{
DiagnosticsManager::GetInstance().emitErrorMessage( 0, "verifier error: bitwise operands can't be ct_int." );
return false;
}
if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )
{
b.push( Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), lhs->type, instr.locationId() } );
return true;
}
// If we are trying to do an operation on a mix of bitvec and int,
// convert the int to a bitvec first.
if( lhs->expr.is_bv() )
{
assert( rhs->expr.is_int() );
b.push( Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), lhs->type, instr.locationId() } );
return true;
}
else
{
assert( lhs->expr.is_int() );
b.push( Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), lhs->type, instr.locationId() } );
return true;
}
return false;
}
template< typename I, typename F >
bool BuildZ3BinBoolExpr( Builder& b, const I& instr, F&& func )
{
auto rhs = b.pop();
if( !rhs )
return false;
auto lhs = b.pop();
if( !lhs )
return false;
if( lhs->expr.get_sort().sort_kind() == rhs->expr.get_sort().sort_kind() )
{
b.push( Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), *EIRToValue( GetValueType< bool >() ), instr.locationId() } );
return true;
}
// If we are trying to do an operation on a mix of bitvec and int,
// convert the int to a bitvec first.
if( lhs->expr.is_bv() )
{
assert( rhs->expr.is_int() );
b.push( Z3Val{ func( lhs->expr, GetAsBitVec( *rhs ), lhs->type ), *EIRToValue( GetValueType< bool >() ), instr.locationId() } );
return true;
}
else
{
assert( lhs->expr.is_int() );
b.push( Z3Val{ func( GetAsBitVec( *lhs ), rhs->expr, lhs->type ), *EIRToValue( GetValueType< bool >() ), instr.locationId() } );
return true;
}
return false;
}
template< typename I, typename F >
bool BuildZ3BinLogicExpr( Builder& b, const I& instr, F&& func )
{
auto rhs = b.pop();
if( !rhs )
return false;
auto lhs = b.pop();
if( !lhs )
return false;
if( !lhs->expr.is_bool() )
return false;
if( !rhs->expr.is_bool() )
return false;
b.push( Z3Val{ func( lhs->expr, rhs->expr, lhs->type ), *EIRToValue( GetValueType< bool >() ), instr.locationId() } );
return true;
}
bool BuildZ3Op( Builder& b, const cir::InstrSeq& is )
{
for( const auto& instr : is )
{
if( !BuildZ3Op( b, instr ) )
return false;
}
return true;
}
template< typename T >
bool BuildZ3Op( Builder& b, const T& instr )
{
return false;
}
bool BuildZ3Op( Builder& b, const Load& instr )
{
optional< Z3Val > zv;
if( auto cstAddr = b.pop< Address >() )
zv = LoadFromAddress( b, *cstAddr, instr.type() );
else if( auto gfa = b.pop< GhostFuncApplication >() )
zv = LoadFromAddress( b, *gfa );
else if( auto symAddr = b.pop< Z3Val >() )
zv = BuildZ3ConstantFromType( b, instr.type(), format( "val{}", b.newUniqueId() ) );
if( !zv )
return false;
if( b.mustLoadAssume() )
ForEachPredicate( b, instr.type(), zv->expr, [&]( auto&& z3expr, auto locId )
{
b.assume( z3expr );
} );
zv->loc = instr.locationId();
b.push( move( *zv ) );
return true;
}
bool BuildZ3Op( Builder& b, const Store& instr )
{
auto zv = b.pop();
if( !zv )
return false;
ForEachPredicate( b, instr.type(), zv->expr, [&]( auto&& z3expr, auto locId )
{
if( !instr.srcLocId().invalid() && !instr.destLocId().invalid() )
{
DiagnosticsContext dc( instr.destLocId(), "...to this." );
DiagnosticsContext dc2( instr.srcLocId(), "When assigning this..." );
b.checkAssertion( z3expr, locId );
}
else
b.checkAssertion( z3expr, locId );
} );
if( auto cstAddr = b.pop< Address >() )
StoreToAddress( b, *cstAddr, move( *zv ) );
else if( auto gfa = b.pop< GhostFuncApplication >() )
StoreToAddress( b, *gfa, move( *zv ) );
else
b.pop();
return true;
}
// Implemented in call.cpp
extern bool BuildZ3Op( Builder& b, const Call& instr );
bool BuildZ3Op( Builder& b, const CreateTemporary& instr )
{
auto zv = b.pop();
if( !zv )
return false;
b.setVar( instr.index(), move( *zv ) );
return true;
}
bool BuildZ3Op( Builder& b, const GetTemporary& instr )
{
auto zv = b.retrieveVar( instr.index() );
if( zv )
{
b.push( move( *zv ) );
return true;
}
auto result = BuildZ3ConstantFromType( b, instr.type(), format( "v{}", instr.index() ) );
b.push( move( result ) );
return true;
}
bool BuildZ3Op( Builder& b, const AllocVar& instr )
{
auto tinfo = TypeCache::GetInstance()->getTypeInfo( b.context(), ValueToEIR( instr.type() ) );
if( !tinfo )
tinfo = TypeCache::GetInstance()->getUninterpretedTypeInfo();
b.setVar( instr.index(), Z3Val{ tinfo->undefined( b ), instr.type(), instr.locationId() } );
return true;
}
// Implemented in phi.cpp
bool BuildZ3Op( Builder& b, const Phi& instr );
// TODO: LoadConstStr. Build a z3 str value.
bool BuildZ3Op( Builder& b, const Not& instr )
{
return BuildZ3UnaryExpr( b, instr, []( auto&& operand )
{
if( operand.is_bool() )
return !operand;
assert( operand.is_bv() );
return ~operand;
} );
}
bool BuildZ3Op( Builder& b, const And& instr )
{
return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
{
if( lhs.is_bool() && rhs.is_bool() )
return lhs && rhs;
auto lhsBV = GetAsBitVec( lhs, type );
auto rhsBV = GetAsBitVec( rhs, type );
return lhsBV & rhsBV;
} );
}
bool BuildZ3Op( Builder& b, const Or& instr )
{
return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
{
if( lhs.is_bool() && rhs.is_bool() )
return lhs || rhs;
auto lhsBV = GetAsBitVec( lhs, type );
auto rhsBV = GetAsBitVec( rhs, type );
return lhsBV | rhsBV;
} );
}
bool BuildZ3Op( Builder& b, const Xor& instr )
{
return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
{
if( lhs.is_bool() && rhs.is_bool() )
return lhs ^ rhs;
auto lhsBV = GetAsBitVec( lhs, type );
auto rhsBV = GetAsBitVec( rhs, type );
return lhsBV ^ rhsBV;
} );
}
bool BuildZ3Op( Builder& b, const Implies& instr )
{
return BuildZ3BinLogicExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
{
return z3::implies( lhs, rhs );
} );
}
bool BuildZ3Op( Builder& b, const Shl& instr )
{
return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
{
auto lhsBV = GetAsBitVec( lhs, type );
auto rhsBV = GetAsBitVec( rhs, type );
return z3::shl( lhsBV, rhsBV );
} );
}
bool BuildZ3Op( Builder& b, const LShr& instr )
{
return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
{
auto lhsBV = GetAsBitVec( lhs, type );
auto rhsBV = GetAsBitVec( rhs, type );
return z3::lshr( lhsBV, rhsBV );
} );
}
bool BuildZ3Op( Builder& b, const AShr& instr )
{
return BuildZ3BinBitwiseExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
{
auto lhsBV = GetAsBitVec( lhs, type );
auto rhsBV = GetAsBitVec( rhs, type );
return z3::ashr( lhsBV, rhsBV );
} );
}
bool BuildZ3Op( Builder& b, const Add& instr )
{
return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs + rhs; } );
}
bool BuildZ3Op( Builder& b, const Sub& instr )
{
return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs - rhs; } );
}
bool BuildZ3Op( Builder& b, const Mul& instr )
{
return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs * rhs; } );
}
bool BuildZ3Op( Builder& b, const UDiv& instr )
{
return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::udiv( lhs, rhs ); } );
}
bool BuildZ3Op( Builder& b, const SDiv& instr )
{
return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs / rhs; } );
}
bool BuildZ3Op( Builder& b, const URem& instr )
{
return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::urem( lhs, rhs ); } );
}
bool BuildZ3Op( Builder& b, const SRem& instr )
{
return BuildZ3BinExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type )
{
if( lhs.is_bv() )
return z3::srem( lhs, rhs );
return z3::rem( lhs, rhs );
} );
}
bool BuildZ3Op( Builder& b, const Eq& instr )
{
return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs == rhs; } );
}
bool BuildZ3Op( Builder& b, const Neq& instr )
{
return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs != rhs; } );
}
bool BuildZ3Op( Builder& b, const UGT& instr )
{
return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::ugt( lhs, rhs ); } );
}
bool BuildZ3Op( Builder& b, const UGE& instr )
{
return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::uge( lhs, rhs ); } );
}
bool BuildZ3Op( Builder& b, const ULT& instr )
{
return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::ult( lhs, rhs ); } );
}
bool BuildZ3Op( Builder& b, const ULE& instr )
{
return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return z3::ule( lhs, rhs ); } );
}
bool BuildZ3Op( Builder& b, const SGT& instr )
{
return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs > rhs; } );
}
bool BuildZ3Op( Builder& b, const SGE& instr )
{
return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs >= rhs; } );
}
bool BuildZ3Op( Builder& b, const SLT& instr )
{
return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs < rhs; } );
}
bool BuildZ3Op( Builder& b, const SLE& instr )
{
return BuildZ3BinBoolExpr( b, instr, []( auto&& lhs, auto&& rhs, auto&& type ) { return lhs <= rhs; } );
}
bool BuildZ3Op( Builder& b, const Assert& instr )
{
auto cond = b.pop();
if( !cond )
return false;
b.checkAssertion( cond->expr, cond->loc );
return true;
}
bool BuildZ3Op( Builder& b, const Placeholder& instr )
{
const auto* expr = b.retrievePlaceholder( instr.name() );
if( expr )
{
b.push( Z3Val{ *expr, *EIRToValue( instr.type() ), instr.locationId() } );
return true;
}
auto result = BuildZ3ConstantFromType( b, instr.type(), format( "p{}", instr.name().str() ) );
b.push( move( result ) );
return true;
}
bool BuildZ3Op( Builder& b, const PHOverrideSet& instr )
{
auto phExpr = b.pop();
if( !phExpr )
return false;
b.setPlaceholder( instr.name(), phExpr->expr );
return true;
}
bool BuildZ3Op( Builder& b, const PHOverrideClear& instr )
{
b.unsetPlaceholder( instr.name() );
return true;
}
bool BuildZ3Op( Builder& b, const Constant& instr )
{
auto v = TryBuildZ3ExprFromValue( b, instr.value() );
if( v )
b.push( move( *v ) );
else
b.push( instr.value() );
return true;
}
bool BuildZ3Op( Builder& b, const VarAddr& instr )
{
b.push( Address( Address::Origin::Stack, instr.varIndex(), instr.locationId() ) );
return true;
}
bool BuildZ3Op( Builder& b, const TempAddr& instr )
{
auto initVal = b.pop();
if( !initVal )
return false;
b.push( Address( Address::Origin::Stack, instr.tempIndex(), instr.locationId() ) );
return !!b.setVar( instr.tempIndex(), move( *initVal ) );
}
bool BuildZ3Op( Builder& b, const Select& instr )
{
if( auto cstBaseLoc = b.pop< Address >() )
{
b.push( Address::Select( move( *cstBaseLoc ), instr.memberIndex(), instr.locationId() ) );
return true;
}
auto symBaseLoc = b.pop();
if( !symBaseLoc )
return false;
b.push( Z3Val
{
z3::concat( symBaseLoc->expr, GetZ3Context().int_val( instr.memberIndex() ).unit() ),
*EIRToValue( GetValueType< builtins::MemLoc >() ),
instr.locationId()
} );
return true;
}
bool BuildZ3Op( Builder& b, const GhostCall& instr )
{
auto gfunc = b.pop< Value >();
if( !gfunc )
return false;
auto argCount = instr.numArgs();
llvm::SmallVector< Z3Val, 16 > args;
args.reserve( argCount );
for( uint32_t argIndex = 0; argIndex < argCount; ++argIndex )
{
auto zv = b.pop();
if( !zv )
return false;
args.emplace_back( move( *zv ) );
}
GhostFuncApplication gfa( move( *gfunc ), instr.locationId() );
gfa.args().reserve( args.size() );
for( auto it = args.rbegin(); it != args.rend(); ++it )
gfa.args().emplace_back( move( *it ) );
b.push( move( gfa ) );
return true;
}
bool BuildZ3Op( Builder& b, const cir::Instruction& instr )
{
return visit( [&]( auto&& e )
{
return BuildZ3Op( b, e );
}, instr.content() );
}
Z3Val BuildZ3ExprFromValue( Builder& b, const Value& val )
{
if( val.isConstant() )
return BuildZ3ValFromConstant( b, val );
if( BuildZ3Op( b, *val.cir() ) )
{
auto result = b.pop();
if( result )
return *result;
}
return BuildZ3ConstantFromType( b, val.type(), format( "val{}", b.newUniqueId() ) );
}
optional< Z3Val > TryBuildZ3ExprFromValue( Builder& b, const Value& val )
{
if( val.isPoison() )
return nullopt;
if( val.isConstant() )
return TryBuildZ3ValFromConstant( b, val );
if( BuildZ3Op( b, *val.cir() ) )
return b.pop();
return TryBuildZ3ConstantFromType( b, val.type(), format( "val{}", b.newUniqueId() ) );
}
}
|
Changes to bs/verify/value.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#ifndef GOOSE_VERIFY_VALUE_H
#define GOOSE_VERIFY_VALUE_H
#include "z3++.h"
#include "cir/cir.h"
namespace goose::verify
{
struct Z3Val
{
z3::expr expr;
eir::Value type;
};
| > | | | | > > > > > > | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#ifndef GOOSE_VERIFY_VALUE_H
#define GOOSE_VERIFY_VALUE_H
#include "z3++.h"
#include "cir/cir.h"
namespace goose::verify
{
struct Z3Val
{
z3::expr expr;
eir::Value type;
LocationId loc;
};
extern Z3Val BuildZ3ValFromConstant( Builder& b, const Value& val );
extern Z3Val BuildZ3ExprFromValue( Builder& b, const Value& val );
extern Z3Val BuildZ3ConstantFromType( Builder& b, const Value& type, const string& name );
extern Z3Val BuildZ3ConstantFromType( Builder& b, const Term& type, const string& name );
extern optional< Z3Val > TryBuildZ3ValFromConstant( Builder& b, const Value& val );
extern optional< Z3Val > TryBuildZ3ExprFromValue( Builder& b, const Value& val );
extern optional< Z3Val > TryBuildZ3ConstantFromType( Builder& b, const Value& type, const string& name );
extern optional< Z3Val > TryBuildZ3ConstantFromType( Builder& b, const Term& type, const string& name );
extern bool BuildZ3Op( Builder& b, const cir::Instruction& instr );
extern bool BuildZ3Op( Builder& b, const cir::InstrSeq& is );
extern z3::expr GetAsBitVec( const z3::expr& expr, const Value& type );
extern z3::expr GetAsBitVec( const Z3Val& zv );
extern z3::expr GetAsInt( const z3::expr& expr, const Value& type );
extern z3::expr GetAsInt( const Z3Val& zv );
|
| ︙ | ︙ |
Changes to bs/verify/vartracker.cpp.
| ︙ | ︙ | |||
12 13 14 15 16 17 18 |
if( vs->size() <= bbIndex )
return nullopt;
return (*vs)[ bbIndex ];
}
| | | > | | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
if( vs->size() <= bbIndex )
return nullopt;
return (*vs)[ bbIndex ];
}
optional< Z3Val > VarTracker::setForBasicBlock( Builder& b, uint32_t bbIndex, uint32_t index, Z3Val&& val )
{
auto* vs = m_varStorage.get( index );
if( !vs )
{
VarState newVS;
newVS.resize( bbIndex + 1 );
auto& vsRef = m_varStorage.set( index, move( newVS ) );
vs = &vsRef;
}
else if( vs->size() <= bbIndex )
vs->resize( bbIndex + 1 );
auto& c = GetZ3Context();
auto predicate = c.bool_const( format( "b{}", bbIndex ).c_str() );
auto lhsExpr = BuildZ3ConstantFromType( b, val.type, format( "v{}_{}_{}", index, bbIndex, b.newUniqueId() ) );
b.add( z3::implies( predicate, lhsExpr.expr == Coerce( val, lhsExpr ) ) );
(*vs)[ bbIndex ] = move( lhsExpr );
return *(*vs)[ bbIndex ];
}
optional< Z3Val > VarTracker::retrieve( Builder& b, uint32_t bbIndex, uint32_t index )
|
| ︙ | ︙ | |||
62 63 64 65 66 67 68 |
auto& c = GetZ3Context();
b.remapper()->forEachIncomingEdge( bbIndex, [&]( auto&& predIndex, auto&& expr )
{
if( auto predVal = retrieve( b, predIndex, index ) )
{
if( !lhsExpr )
| | | | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
auto& c = GetZ3Context();
b.remapper()->forEachIncomingEdge( bbIndex, [&]( auto&& predIndex, auto&& expr )
{
if( auto predVal = retrieve( b, predIndex, index ) )
{
if( !lhsExpr )
lhsExpr = BuildZ3ConstantFromType( b, predVal->type, format( "v{}_{}_{}", index, bbIndex, b.newUniqueId() ) );
b.add( z3::implies( c.bool_const( format( "e{}_{}", predIndex, bbIndex ).c_str() ),
lhsExpr->expr == Coerce( *predVal, *lhsExpr ) ) );
}
} );
}
if( !lhsExpr )
return nullopt;
return setForBasicBlock( b, bbIndex, index, move( *lhsExpr ) );
}
optional< Z3Val > VarTracker::set( Builder& b, uint32_t index, Z3Val&& val )
{
return setForBasicBlock( b, b.currentBBIndex(), index, move( val ) );
}
|
Changes to bs/verify/vartracker.h.
1 2 3 4 5 6 7 8 9 10 11 |
#ifndef GOOSE_VERIFY_VARTRACKER_H
#define GOOSE_VERIFY_VARTRACKER_H
namespace goose::verify
{
class Builder;
class VarTracker
{
public:
optional< Z3Val > retrieve( Builder& b, uint32_t bbIndex, uint32_t index );
| > > > > | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#ifndef GOOSE_VERIFY_VARTRACKER_H
#define GOOSE_VERIFY_VARTRACKER_H
namespace goose::verify
{
class Builder;
class VarTracker
{
public:
VarTracker( size_t size ) :
m_varStorage( size )
{}
optional< Z3Val > retrieve( Builder& b, uint32_t bbIndex, uint32_t index );
optional< Z3Val > set( Builder& b, uint32_t index, Z3Val&& val );
private:
optional< Z3Val > getForBasicBlock( uint32_t bbIndex, uint32_t index ) const;
optional< Z3Val > setForBasicBlock( Builder& b, uint32_t bbIndex, uint32_t index, Z3Val&& val );
// For each variable, store its last value as a z3 expression.
using VarState = llvm::SmallVector< optional< Z3Val >, 16 >;
using VarStorage = cir::TempStorage< VarState >;
VarStorage m_varStorage;
};
|
| ︙ | ︙ |
Changes to bs/verify/verify.h.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
{
using namespace eir;
using namespace cir;
class Builder;
extern z3::context& GetZ3Context();
| < | > | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
{
using namespace eir;
using namespace cir;
class Builder;
extern z3::context& GetZ3Context();
extern bool VerifyInstrSeq( const sema::Context& c, const InstrSeq& is );
}
#include "helpers.h"
#include "verifyviz.h"
#include "type.h"
#include "value.h"
#include "ghostfunc.h"
#include "storage.h"
#include "stack.h"
#include "remapper.h"
#include "vartracker.h"
#include "gfctracker.h"
#include "builder.h"
#include "propositions.h"
#include "func.h"
#endif
|
Changes to lib/prelude/ref_verification.g0.
| ︙ | ︙ | |||
9 10 11 12 13 14 15 |
Value v
uint(32) i = 1
while i < count
{
GetProposition( predicates, i, v )
| > | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
Value v
uint(32) i = 1
while i < count
{
GetProposition( predicates, i, v )
AppendValueInstr( result, v )
AppendValueInstr( result, MkInstr( InstrOpCodeAnd, GetValueLocation( v ) ) )
i += 1
}
return result
}
void EmitPredicatesEquivalenceCheck( Context c, LocationId argLoc, Value refType1, Value refType2 )
|
| ︙ | ︙ | |||
47 48 49 50 51 52 53 |
if !ContextGetCFG( c, cfg )
return
BasicBlock bb
if !GetCFGCurrentBasicBlock( cfg, bb )
return
| > > | < | | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
if !ContextGetCFG( c, cfg )
return
BasicBlock bb
if !GetCFGCurrentBasicBlock( cfg, bb )
return
AppendBasicBlockInstr( bb, lhsExpr )
AppendBasicBlockInstr( bb, rhsExpr )
AppendBasicBlockInstr( bb, MkInstr( InstrOpCodeEq, argLoc ) )
AppendBasicBlockInstr( bb, MkInstr( InstrOpCodeAssert, argLoc ) )
PushDiagnosticsContext( GetValueLocation( refType2 ), "...to a mutable reference parameter of this type.", true )
PushDiagnosticsContext( GetValueLocation( refType1 ), "When passing a mutable reference to this type..." )
DefineCustomDiagnostic( argLoc, MkStringId( "assert" ), "the argument's and the parameter's type predicates are not equivalent." )
PopDiagnosticsContext()
|
| ︙ | ︙ | |||
78 79 80 81 82 83 84 |
if !GetCFGCurrentBasicBlock( cfg, bb )
return
RefTypeDesc rt
if !EIRToRefTypeDesc( GetValueType( arg ), rt )
return
| | < | > > > > | < < < < > | > > | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
if !GetCFGCurrentBasicBlock( cfg, bb )
return
RefTypeDesc rt
if !EIRToRefTypeDesc( GetValueType( arg ), rt )
return
InstrSeq refCIR
if !GetValueCIR( arg, refCIR )
return
// Create a dereference of the arg to fill the predicates' @val placeholder
var referedType, var refBhv = UnpackRefTypeDesc( rt )
var aLoc = GetValueLocation( arg )
AppendBasicBlockInstr( bb, refCIR )
AppendBasicBlockInstr( bb, MkInstr( InstrOpCodeLoad, referedType, aLoc ) )
AppendBasicBlockInstr( bb, MkInstr( InstrOpCodePHOverrideSet, MkStringId( "@val" ), aLoc ) )
PushDiagnosticsContext( GetValueLocation( refType ), "...to a reference parameter of this type.", true )
PushDiagnosticsContext( GetValueLocation( arg ), "When passing this..." )
Value p
var count = GetPropositionsCount( pred )
uint(32) i = 0
while i < count
{
GetProposition( pred, i, p )
var pLoc = GetValueLocation( p )
AppendBasicBlockInstr( bb, p )
AppendBasicBlockInstr( bb, MkInstr( InstrOpCodeAssert, pLoc ) )
DefineCustomDiagnostic( pLoc, MkStringId( "assert" ) )
i += 1
}
AppendBasicBlockInstr( bb, MkInstr( InstrOpCodePHOverrideClear, MkStringId( "@val" ), aLoc ) )
PopDiagnosticsContext()
PopDiagnosticsContext()
}
// When passing a mutref to a mutref, we need to verify that their predicates are equivalent.
intrinsic mut ref $$T _ConvertFuncArg( mut ref $$T arg, mut ref $$U )
|
| ︙ | ︙ |
Changes to tests/g0/codegen/arithops.g0.
1 2 3 4 |
var module = CGModuleCreate( "arith ops test" )
using entryPoint = void( uint( 8 ) a, uint( 8 ) b , sint( 16 ) c, sint( 16 ) d ) requires [b!=0 d!=0] {
-c
| | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
var module = CGModuleCreate( "arith ops test" )
using entryPoint = void( uint( 8 ) a, uint( 8 ) b , sint( 16 ) c, sint( 16 ) d ) requires [b!=0 d!=0] {
-c
var v1 = a + b
var v2 = a - b
var v3 = a * b
var v4 = a / b
var v5 = c / d
var v6 = a % b
var v7 = c % d
}
CGGenerateFunction( module, entryPoint, "main" )
CGModuleEmitLLVMIr( module, "tests/g0/codegen/arithops.ll" )
|
Changes to tests/g0/codegen/arithops.ll.
1 2 3 4 5 6 7 8 9 10 11 12 |
; ModuleID = 'arith ops test'
source_filename = "arith ops test"
define void @main(i8 %0, i8 %1, i16 %2, i16 %3) {
%5 = alloca i8, align 1
%6 = alloca i8, align 1
%7 = alloca i16, align 2
%8 = alloca i16, align 2
store i8 %0, i8* %5, align 1
store i8 %1, i8* %6, align 1
store i16 %2, i16* %7, align 2
store i16 %3, i16* %8, align 2
| > > > > > > > < < | | | > | | | > | | | > | | | > | | | > | | | > | | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
; ModuleID = 'arith ops test'
source_filename = "arith ops test"
define void @main(i8 %0, i8 %1, i16 %2, i16 %3) {
%5 = alloca i8, align 1
%6 = alloca i8, align 1
%7 = alloca i16, align 2
%8 = alloca i16, align 2
%9 = alloca i8, align 1
%10 = alloca i8, align 1
%11 = alloca i8, align 1
%12 = alloca i8, align 1
%13 = alloca i16, align 2
%14 = alloca i8, align 1
%15 = alloca i16, align 2
store i8 %0, i8* %5, align 1
store i8 %1, i8* %6, align 1
store i16 %2, i16* %7, align 2
store i16 %3, i16* %8, align 2
%16 = load i8, i8* %5, align 1
%17 = load i8, i8* %6, align 1
%18 = add i8 %16, %17
store i8 %18, i8* %9, align 1
%19 = load i8, i8* %5, align 1
%20 = load i8, i8* %6, align 1
%21 = sub i8 %19, %20
store i8 %21, i8* %10, align 1
%22 = load i8, i8* %5, align 1
%23 = load i8, i8* %6, align 1
%24 = mul i8 %22, %23
store i8 %24, i8* %11, align 1
%25 = load i8, i8* %5, align 1
%26 = load i8, i8* %6, align 1
%27 = udiv i8 %25, %26
store i8 %27, i8* %12, align 1
%28 = load i16, i16* %7, align 2
%29 = load i16, i16* %8, align 2
%30 = sdiv i16 %28, %29
store i16 %30, i16* %13, align 2
%31 = load i8, i8* %5, align 1
%32 = load i8, i8* %6, align 1
%33 = urem i8 %31, %32
store i8 %33, i8* %14, align 1
%34 = load i16, i16* %7, align 2
%35 = load i16, i16* %8, align 2
%36 = srem i16 %34, %35
store i16 %36, i16* %15, align 2
ret void
}
|
Changes to tests/g0/codegen/bitwiseops.g0.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 |
}
using entryPoint = void( uint( 8 ) a, uint( 8 ) b , sint( 16 ) c, sint( 16 ) d ) {
lomarf( a, b )
lomarf( 219, d )
lomarf( d, 69 )
| | | | | | | | | | | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
}
using entryPoint = void( uint( 8 ) a, uint( 8 ) b , sint( 16 ) c, sint( 16 ) d ) {
lomarf( a, b )
lomarf( 219, d )
lomarf( d, 69 )
var v1 = a ^ b
var v2 = c ^ d
var v3 = c ^ 1337
var v4 = 1337 ^ c
var v5 = ~d
var v6 = a | b
var v7 = a & b
var v8 = a << 4
var v9 = a >> 4
var v10 = c << 4
var v11 = c >> 4
}
CGGenerateFunction( module, entryPoint, "main" )
CGModuleEmitLLVMIr( module, "tests/g0/codegen/bitwiseops.ll" )
|
Changes to tests/g0/codegen/bitwiseops.ll.
1 2 3 4 5 6 7 8 9 10 11 12 |
; ModuleID = 'bitwise ops test'
source_filename = "bitwise ops test"
define void @main(i8 %0, i8 %1, i16 %2, i16 %3) {
%5 = alloca i8, align 1
%6 = alloca i8, align 1
%7 = alloca i16, align 2
%8 = alloca i16, align 2
store i8 %0, i8* %5, align 1
store i8 %1, i8* %6, align 1
store i16 %2, i16* %7, align 2
store i16 %3, i16* %8, align 2
| > > > > > > > > > > > | | | | | | | | | | > | | | > | | > | | > | | > | | | > | | | > | | > | | > | | > | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
; ModuleID = 'bitwise ops test'
source_filename = "bitwise ops test"
define void @main(i8 %0, i8 %1, i16 %2, i16 %3) {
%5 = alloca i8, align 1
%6 = alloca i8, align 1
%7 = alloca i16, align 2
%8 = alloca i16, align 2
%9 = alloca i8, align 1
%10 = alloca i16, align 2
%11 = alloca i16, align 2
%12 = alloca i16, align 2
%13 = alloca i16, align 2
%14 = alloca i8, align 1
%15 = alloca i8, align 1
%16 = alloca i8, align 1
%17 = alloca i8, align 1
%18 = alloca i16, align 2
%19 = alloca i16, align 2
store i8 %0, i8* %5, align 1
store i8 %1, i8* %6, align 1
store i16 %2, i16* %7, align 2
store i16 %3, i16* %8, align 2
%20 = load i8, i8* %5, align 1
%21 = load i8, i8* %6, align 1
%22 = call i8 @"_2_3s2:g0s3#20#s6:lomarf_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i8i0_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i8i0hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i8i0h$3nPi0"(i8 %20, i8 %21)
%23 = load i16, i16* %8, align 2
%24 = call i16 @"_2_3s2:g0s3#20#s6:lomarf_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i10i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i10i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i10i1h$3nPi0"(i16 219, i16 %23)
%25 = load i16, i16* %8, align 2
%26 = call i16 @"_2_3s2:g0s3#20#s6:lomarf_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i10i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i10i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i10i1h$3nPi0"(i16 %25, i16 69)
%27 = load i8, i8* %5, align 1
%28 = load i8, i8* %6, align 1
%29 = xor i8 %27, %28
store i8 %29, i8* %9, align 1
%30 = load i16, i16* %7, align 2
%31 = load i16, i16* %8, align 2
%32 = xor i16 %30, %31
store i16 %32, i16* %10, align 2
%33 = load i16, i16* %7, align 2
%34 = xor i16 %33, 1337
store i16 %34, i16* %11, align 2
%35 = load i16, i16* %7, align 2
%36 = xor i16 1337, %35
store i16 %36, i16* %12, align 2
%37 = load i16, i16* %8, align 2
%38 = xor i16 %37, -1
store i16 %38, i16* %13, align 2
%39 = load i8, i8* %5, align 1
%40 = load i8, i8* %6, align 1
%41 = or i8 %39, %40
store i8 %41, i8* %14, align 1
%42 = load i8, i8* %5, align 1
%43 = load i8, i8* %6, align 1
%44 = and i8 %42, %43
store i8 %44, i8* %15, align 1
%45 = load i8, i8* %5, align 1
%46 = shl i8 %45, 4
store i8 %46, i8* %16, align 1
%47 = load i8, i8* %5, align 1
%48 = lshr i8 %47, 4
store i8 %48, i8* %17, align 1
%49 = load i16, i16* %7, align 2
%50 = shl i16 %49, 4
store i16 %50, i16* %18, align 2
%51 = load i16, i16* %7, align 2
%52 = ashr i16 %51, 4
store i16 %52, i16* %19, align 2
ret void
}
define private i8 @"_2_3s2:g0s3#20#s6:lomarf_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i8i0_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i8i0hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i8i0h$3nPi0"(i8 %0, i8 %1) {
%3 = alloca i8, align 1
%4 = alloca i8, align 1
store i8 %0, i8* %3, align 1
|
| ︙ | ︙ |
Changes to tests/g0/codegen/comparisonops.g0.
1 2 3 |
var module = CGModuleCreate( "comparison ops test" )
using entryPoint = void( uint( 8 ) a, uint( 8 ) b , sint( 16 ) c, sint( 16 ) d ) {
| > > | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var module = CGModuleCreate( "comparison ops test" )
using entryPoint = void( uint( 8 ) a, uint( 8 ) b , sint( 16 ) c, sint( 16 ) d ) {
bool x = false
x = a == b
x = a != b
x = a > b
x = a < b
x = a >= b
x = a <= b
x = c > d
x = c < d
x = c >= d
x = c <= d
}
CGGenerateFunction( module, entryPoint, "main" )
CGModuleEmitLLVMIr( module, "tests/g0/codegen/comparisonops.ll" )
|
Changes to tests/g0/codegen/comparisonops.ll.
1 2 3 4 5 6 7 8 9 10 11 12 |
; ModuleID = 'comparison ops test'
source_filename = "comparison ops test"
define void @main(i8 %0, i8 %1, i16 %2, i16 %3) {
%5 = alloca i8, align 1
%6 = alloca i8, align 1
%7 = alloca i16, align 2
%8 = alloca i16, align 2
store i8 %0, i8* %5, align 1
store i8 %1, i8* %6, align 1
store i16 %2, i16* %7, align 2
store i16 %3, i16* %8, align 2
| > > | | | > | | | > | | | > | | | > | | | > | | | > | | | > | | | > | | | > | | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
; ModuleID = 'comparison ops test'
source_filename = "comparison ops test"
define void @main(i8 %0, i8 %1, i16 %2, i16 %3) {
%5 = alloca i8, align 1
%6 = alloca i8, align 1
%7 = alloca i16, align 2
%8 = alloca i16, align 2
%9 = alloca i1, align 1
store i8 %0, i8* %5, align 1
store i8 %1, i8* %6, align 1
store i16 %2, i16* %7, align 2
store i16 %3, i16* %8, align 2
store i1 false, i1* %9, align 1
%10 = load i8, i8* %5, align 1
%11 = load i8, i8* %6, align 1
%12 = icmp eq i8 %10, %11
store i1 %12, i1* %9, align 1
%13 = load i8, i8* %5, align 1
%14 = load i8, i8* %6, align 1
%15 = icmp ne i8 %13, %14
store i1 %15, i1* %9, align 1
%16 = load i8, i8* %5, align 1
%17 = load i8, i8* %6, align 1
%18 = icmp ugt i8 %16, %17
store i1 %18, i1* %9, align 1
%19 = load i8, i8* %5, align 1
%20 = load i8, i8* %6, align 1
%21 = icmp ult i8 %19, %20
store i1 %21, i1* %9, align 1
%22 = load i8, i8* %5, align 1
%23 = load i8, i8* %6, align 1
%24 = icmp uge i8 %22, %23
store i1 %24, i1* %9, align 1
%25 = load i8, i8* %5, align 1
%26 = load i8, i8* %6, align 1
%27 = icmp ule i8 %25, %26
store i1 %27, i1* %9, align 1
%28 = load i16, i16* %7, align 2
%29 = load i16, i16* %8, align 2
%30 = icmp sgt i16 %28, %29
store i1 %30, i1* %9, align 1
%31 = load i16, i16* %7, align 2
%32 = load i16, i16* %8, align 2
%33 = icmp slt i16 %31, %32
store i1 %33, i1* %9, align 1
%34 = load i16, i16* %7, align 2
%35 = load i16, i16* %8, align 2
%36 = icmp sge i16 %34, %35
store i1 %36, i1* %9, align 1
%37 = load i16, i16* %7, align 2
%38 = load i16, i16* %8, align 2
%39 = icmp sle i16 %37, %38
store i1 %39, i1* %9, align 1
ret void
}
|
Changes to tests/g0/codegen/if.ll.
| ︙ | ︙ | |||
15 16 17 18 19 20 21 |
define private void @"_2_3s2:g0s3#20#s3:meh_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0v_2q_2_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplhs1:_n_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplh$3nPi0"(i1 %0, i1 %1) {
%3 = alloca i1, align 1
%4 = alloca i1, align 1
store i1 %0, i1* %3, align 1
store i1 %1, i1* %4, align 1
%5 = load i1, i1* %3, align 1
| | | | | | | | | > | | < | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
define private void @"_2_3s2:g0s3#20#s3:meh_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0v_2q_2_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplhs1:_n_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplh$3nPi0"(i1 %0, i1 %1) {
%3 = alloca i1, align 1
%4 = alloca i1, align 1
store i1 %0, i1* %3, align 1
store i1 %1, i1* %4, align 1
%5 = load i1, i1* %3, align 1
br i1 %5, label %6, label %9
6: ; preds = %2
%7 = load i1, i1* %4, align 1
%8 = xor i1 %7, true
br i1 %8, label %10, label %11
9: ; preds = %2
call void @puts([4 x i8]* @.str3)
br label %13
10: ; preds = %6
call void @puts([7 x i8]* @.str0)
br label %12
11: ; preds = %6
call void @puts([5 x i8]* @.str2)
br label %12
12: ; preds = %11, %10
br label %13
13: ; preds = %9, %12
call void @puts([3 x i8]* @.str1)
ret void
}
declare void @puts(i8*)
|
Changes to tests/g0/codegen/logicops.ll.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 |
define private i1 @"_2_3s2:g0s3#20#s6:lomarf_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_4T_2RPpl_2q_2_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplhs1:_n_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplh$3nPi0"(i1 %0, i1 %1) {
%3 = alloca i1, align 1
%4 = alloca i1, align 1
store i1 %0, i1* %3, align 1
store i1 %1, i1* %4, align 1
%5 = load i1, i1* %3, align 1
| | | | | | | | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
define private i1 @"_2_3s2:g0s3#20#s6:lomarf_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_4T_2RPpl_2q_2_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplhs1:_n_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplh$3nPi0"(i1 %0, i1 %1) {
%3 = alloca i1, align 1
%4 = alloca i1, align 1
store i1 %0, i1* %3, align 1
store i1 %1, i1* %4, align 1
%5 = load i1, i1* %3, align 1
br i1 %5, label %6, label %8
6: ; preds = %8, %2
%7 = phi i1 [ true, %2 ], [ %9, %8 ]
ret i1 %7
8: ; preds = %2
%9 = load i1, i1* %4, align 1
br label %6
}
define private i1 @"_2_3s2:g0s3#20#s4:blah_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_4T_2RPpl_2q_2_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplhs1:_n_5Vr_5Vo_5Vo_3ti1i0_4T_2RPplh$3nPi0"(i1 %0, i1 %1) {
%3 = alloca i1, align 1
%4 = alloca i1, align 1
store i1 %0, i1* %3, align 1
store i1 %1, i1* %4, align 1
|
| ︙ | ︙ |
Changes to tests/g0/codegen/while.ll.
1 2 3 4 5 6 7 8 9 10 11 12 |
; ModuleID = 'while test'
source_filename = "while test"
define void @main() {
%1 = alloca i32, align 4
%2 = alloca i1, align 1
%3 = alloca i1, align 1
store i32 0, i32* %1, align 4
store i1 true, i1* %2, align 1
store i1 true, i1* %3, align 1
br label %4
| | | | | > > > | | | | | | | | < < < | | | | | < < < | | > > | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
; ModuleID = 'while test'
source_filename = "while test"
define void @main() {
%1 = alloca i32, align 4
%2 = alloca i1, align 1
%3 = alloca i1, align 1
store i32 0, i32* %1, align 4
store i1 true, i1* %2, align 1
store i1 true, i1* %3, align 1
br label %4
4: ; preds = %17, %10, %0
%5 = load i32, i32* %1, align 4
%6 = icmp ult i32 %5, 10
br i1 %6, label %7, label %9
7: ; preds = %4
%8 = load i1, i1* %2, align 1
br i1 %8, label %10, label %11
9: ; preds = %4
ret void
10: ; preds = %7
br label %4
11: ; preds = %7
br label %12
12: ; preds = %21, %11
%13 = load i32, i32* %1, align 4
%14 = icmp ugt i32 %13, 50
br i1 %14, label %15, label %17
15: ; preds = %12
%16 = load i1, i1* %3, align 1
br i1 %16, label %20, label %21
17: ; preds = %20, %12
%18 = load i32, i32* %1, align 4
%19 = sub i32 %18, 2
store i32 %19, i32* %1, align 4
br label %4
20: ; preds = %15
br label %17
21: ; preds = %15
%22 = load i32, i32* %1, align 4
%23 = add i32 %22, 33
store i32 %23, i32* %1, align 4
br label %12
}
|
Changes to tests/g0/function/c-fail-calling-compiletime-func.txt.
1 | tests/g0/function/c-fail-calling-compiletime-func.g0: | < < < < < < < | < > | 1 2 3 4 |
tests/g0/function/c-fail-calling-compiletime-func.g0:
8 | put( 'g' )
| ^^^
| Error: constants with compile-time types are not supported by code generation.
|
Changes to tests/g0/verification/c-fail-test-4.txt.
1 2 3 4 5 6 7 8 |
tests/g0/verification/c-fail-test-4.g0:
3 | ensures [ @result > 0 ]
| ^^^^^^^^^^^
| Error: this condition may not be met.
...
7 | return b
| ~
| When returning this.
| > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
tests/g0/verification/c-fail-test-4.g0:
3 | ensures [ @result > 0 ]
| ^^^^^^^^^^^
| Error: this condition may not be met.
...
7 | return b
| ~
| When returning this.
tests/g0/verification/c-fail-test-4.g0:
3 | ensures [ @result > 0 ]
| ^^^^^^^^^^^
| Error: this condition may not be met.
...
16 | return ta - tb
| ~~~~~~~
| When returning this.
|
Changes to tests/g0/verification/c-fail-test-7.txt.
1 2 3 |
tests/g0/verification/c-fail-test-7.g0:
15 | sint(32)[!=0] testFunc2( sint(32)[!=0] a )
| ^^^
| | | | 1 2 3 4 5 6 7 8 |
tests/g0/verification/c-fail-test-7.g0:
15 | sint(32)[!=0] testFunc2( sint(32)[!=0] a )
| ^^^
| Error: this condition may not be met.
...
21 | testFunc2( 0 )
| ~~~~~~~~~~~~~~
| At this call.
|
Changes to tests/g0/verification/misc/c-fail-mandelbrot-bad-3.txt.
1 2 3 |
tests/g0/verification/misc/c-fail-mandelbrot-bad-3.g0:
9 | s32 IntToFixedWidthDec( s32[<(1<<12) >(-1<<12)] i )
| ^^^^^^^^^
| | | | 1 2 3 4 5 6 7 8 |
tests/g0/verification/misc/c-fail-mandelbrot-bad-3.g0:
9 | s32 IntToFixedWidthDec( s32[<(1<<12) >(-1<<12)] i )
| ^^^^^^^^^
| Error: this condition may not be met.
...
17 | using minU = IntToFixedWidthDec( -20000 ) // Intentional error
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| At this call.
|
Changes to tests/g0/verification/misc/c-fail-mandelbrot-bad-4.txt.
1 2 3 |
tests/g0/verification/misc/c-fail-mandelbrot-bad-4.g0:
9 | s32 IntToFixedWidthDec( s32[<(1<<12) >(-1<<12)] i )
| ^^^^^^^^
| | | | 1 2 3 4 5 6 7 8 |
tests/g0/verification/misc/c-fail-mandelbrot-bad-4.g0:
9 | s32 IntToFixedWidthDec( s32[<(1<<12) >(-1<<12)] i )
| ^^^^^^^^
| Error: this condition may not be met.
...
18 | using maxU = IntToFixedWidthDec( 100000 ) // Intentional error
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| At this call.
|
Changes to tests/g0/verification/z3gen/e-z3gen-test-1.txt.
|
| < < < < < < | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | === Begin function verification trace === (=> b1 (= v0_1_113 p0)) (=> b1 (= v1_1_115 u114)) (=> b1 (= v1_1_116 v0_1_113)) (= e1_2 (and b1 (distinct v1_1_116 5))) (= b2 e1_2) (=> b2 (= v1_2_117 5)) (= e1_3 (and b1 (not (distinct v1_1_116 5)))) (= e2_3 (and b2 b2)) (= b3 (or e1_3 e2_3)) (= e3_4 (and b3 b3)) (= b4 e3_4) (=> e1_3 (= v1_3_118 v1_1_116)) (=> e2_3 (= v1_3_118 v1_2_117)) (=> b3 (= v1_3_119 v1_3_118)) (=> e3_4 (= v1_4_120 v1_3_119)) (=> b4 (= v1_4_121 v1_4_120)) check_unsat (and b4 (not (= v1_4_121 5))) (= e4_5 (and b4 b4)) (= b5 e4_5) === End function verification trace === === Begin function verification trace === (=> b1 (= v0_1_122 1)) === End function verification trace === |
Changes to tests/g0/verification/z3gen/e-z3gen-test-2.txt.
|
| < < < < < < < < < < < < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | === Begin function verification trace === (=> b1 (= v0_1_113 p0)) (=> b1 (= v1_1_114 p1)) assume (distinct v0_1_113 v1_1_114) (=> b1 (= v3_1_116 u115)) (=> b1 (= v3_1_117 v0_1_113)) (=> b1 (= v4_1_119 u118)) (=> b1 (= v4_1_120 v1_1_114)) (=> b1 (= v5_1_122 u121)) (=> b1 (= v5_1_123 v3_1_117)) (=> b1 (= v6_1_125 u124)) (=> b1 (= v6_1_126 v4_1_120)) (= e1_2 (and b1 (< v5_1_123 v6_1_126))) (= b2 e1_2) (=> b2 (= v8_2_128 u127)) (=> e1_2 (= v6_2_129 v6_1_126)) (=> b2 (= v6_2_130 v6_2_129)) (=> b2 (= v8_2_131 v6_2_130)) (=> b2 (= v9_2_133 u132)) (=> e1_2 (= v5_2_134 v5_1_123)) (=> b2 (= v5_2_135 v5_2_134)) (=> b2 (= v9_2_136 v5_2_135)) (=> b2 (= v5_2_137 v8_2_131)) (=> b2 (= v6_2_138 v9_2_136)) (= e1_3 (and b1 (not (< v5_1_123 v6_1_126)))) (= e2_3 (and b2 b2)) (= b3 (or e1_3 e2_3)) (= e3_4 (and b3 b3)) (= b4 e3_4) (=> e1_3 (= v5_3_139 v5_1_123)) (=> e2_3 (= v5_3_139 v5_2_137)) (=> b3 (= v5_3_140 v5_3_139)) (=> e3_4 (= v5_4_141 v5_3_140)) (=> b4 (= v5_4_142 v5_4_141)) (=> e1_3 (= v6_3_143 v6_1_126)) (=> e2_3 (= v6_3_143 v6_2_138)) (=> b3 (= v6_3_144 v6_3_143)) (=> e3_4 (= v6_4_145 v6_3_144)) (=> b4 (= v6_4_146 v6_4_145)) check_unsat (and b4 (not (> v5_4_142 v6_4_146))) (= e4_5 (and b4 b4)) (= b5 e4_5) === End function verification trace === === Begin function verification trace === (=> b1 (= v1_1_147 1)) (=> b1 (= v0_1_148 2)) check_unsat (not (distinct v0_1_148 v1_1_147)) === End function verification trace === |
Changes to tests/g0/verification/z3gen/e-z3gen-test-3.txt.
|
| < < < < < < < < < < < < < < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | === Begin function verification trace === (=> b1 (= v0_1_113 p0)) (=> b1 (= v1_1_114 p1)) assume (distinct v0_1_113 v1_1_114) (=> b1 (= v3_1_116 u115)) (=> b1 (= v3_1_117 v0_1_113)) (=> b1 (= v4_1_119 u118)) (=> b1 (= v4_1_120 v1_1_114)) (=> b1 (= v5_1_122 u121)) (=> b1 (= v5_1_123 v3_1_117)) (=> b1 (= v6_1_125 u124)) (=> b1 (= v6_1_126 v4_1_120)) (= e1_2 (and b1 (< v5_1_123 v6_1_126))) (= b2 e1_2) (=> b2 (= v8_2_128 u127)) (=> e1_2 (= v6_2_129 v6_1_126)) (=> b2 (= v6_2_130 v6_2_129)) (=> b2 (= v8_2_131 v6_2_130)) (=> b2 (= v9_2_133 u132)) (=> e1_2 (= v5_2_134 v5_1_123)) (=> b2 (= v5_2_135 v5_2_134)) (=> b2 (= v9_2_136 v5_2_135)) (=> b2 (= v5_2_137 v8_2_131)) (=> b2 (= v6_2_138 v9_2_136)) (= e1_3 (and b1 (not (< v5_1_123 v6_1_126)))) (= e2_3 (and b2 b2)) (= b3 (or e1_3 e2_3)) (=> e1_3 (= v5_3_139 v5_1_123)) (=> e2_3 (= v5_3_139 v5_2_137)) (=> b3 (= v5_3_140 v5_3_139)) (=> e1_3 (= v6_3_141 v6_1_126)) (=> e2_3 (= v6_3_141 v6_2_138)) (=> b3 (= v6_3_142 v6_3_141)) check_unsat (and b3 (not (> (- v5_3_140 v6_3_142) 0))) === End function verification trace === === Checking instr seq === (=> b1 (= v1_1_144 1)) (=> b1 (= v0_1_145 2)) check_unsat (not (distinct v0_1_145 v1_1_144)) assume (> r143 0) === Begin function verification trace === === End function verification trace === |
Changes to tests/g0/verification/z3gen/e-z3gen-test-4.txt.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < < | | > | < | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | === Begin function verification trace === (=> b1 (= v0_1_113 p0)) (=> b1 (= v1_1_114 p1)) assume (distinct v0_1_113 v1_1_114) (=> b1 (= v3_1_116 u115)) (=> b1 (= v3_1_117 v0_1_113)) (=> b1 (= v4_1_119 u118)) (=> b1 (= v4_1_120 v1_1_114)) (=> b1 (= v5_1_122 u121)) (=> b1 (= v5_1_123 v3_1_117)) (=> b1 (= v6_1_125 u124)) (=> b1 (= v6_1_126 v4_1_120)) (= e1_2 (and b1 (< v5_1_123 v6_1_126))) (= b2 e1_2) (=> b2 (= v8_2_128 u127)) (=> e1_2 (= v6_2_129 v6_1_126)) (=> b2 (= v6_2_130 v6_2_129)) (=> b2 (= v8_2_131 v6_2_130)) (=> b2 (= v9_2_133 u132)) (=> e1_2 (= v5_2_134 v5_1_123)) (=> b2 (= v5_2_135 v5_2_134)) (=> b2 (= v9_2_136 v5_2_135)) (=> b2 (= v5_2_137 v8_2_131)) (=> b2 (= v6_2_138 v9_2_136)) (= e1_3 (and b1 (not (< v5_1_123 v6_1_126)))) (= e2_3 (and b2 b2)) (= b3 (or e1_3 e2_3)) (=> e1_3 (= v5_3_139 v5_1_123)) (=> e2_3 (= v5_3_139 v5_2_137)) (=> b3 (= v5_3_140 v5_3_139)) (=> e1_3 (= v6_3_141 v6_1_126)) (=> e2_3 (= v6_3_141 v6_2_138)) (=> b3 (= v6_3_142 v6_3_141)) check_unsat (and b3 (not (> (- v5_3_140 v6_3_142) 0))) === End function verification trace === === Begin function verification trace === (=> b1 (= v0_1_143 p0)) assume (distinct v0_1_143 0) (=> b1 (= v1_1_145 (* 2 v0_1_143))) (=> b1 (= v0_1_146 v0_1_143)) check_unsat (not (distinct v0_1_146 v1_1_145)) assume (> r144 0) check_unsat (not (distinct r144 0)) === End function verification trace === === Checking instr seq === (=> b1 (= v0_1_148 5)) check_unsat (not (distinct v0_1_148 0)) assume (distinct r147 0) === Begin function verification trace === === End function verification trace === |
Changes to tests/g0/verification/z3gen/e-z3gen-test-5.txt.
|
| < < < < < < < < < < < < < < < < < < | | | | | | | | | | | | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | === Begin function verification trace === (=> b1 (= v0_1_113 p0)) (=> b1 (= v1_1_114 p1)) assume (distinct v0_1_113 v1_1_114) (=> b1 (= v2_1_117 (tupsort0 u115 u116))) (=> b1 (= v2_1_118 (tupsort0 v0_1_113 (tupsort0_1 v2_1_117)))) (=> b1 (= v2_1_119 (tupsort0 (tupsort0_0 v2_1_118) v1_1_114))) (= e1_2 (and b1 (< (tupsort0_0 v2_1_119) (tupsort0_1 v2_1_119)))) (= b2 e1_2) (=> b2 (= v4_2_121 u120)) (=> e1_2 (= v2_2_122 v2_1_119)) (=> b2 (= v2_2_123 v2_2_122)) (=> b2 (= v4_2_124 (tupsort0_1 v2_2_123))) (=> b2 (= v5_2_126 u125)) (=> b2 (= v5_2_127 (tupsort0_0 v2_2_123))) (=> b2 (= v2_2_128 (tupsort0 v4_2_124 (tupsort0_1 v2_2_123)))) (=> b2 (= v2_2_129 (tupsort0 (tupsort0_0 v2_2_128) v5_2_127))) (let ((a!1 (and b1 (not (< (tupsort0_0 v2_1_119) (tupsort0_1 v2_1_119)))))) (= e1_3 a!1)) (= e2_3 (and b2 b2)) (= b3 (or e1_3 e2_3)) (= e3_4 (and b3 b3)) (= b4 e3_4) (=> e1_3 (= v2_3_130 v2_1_119)) (=> e2_3 (= v2_3_130 v2_2_129)) (=> b3 (= v2_3_131 v2_3_130)) (=> e3_4 (= v2_4_132 v2_3_131)) (=> b4 (= v2_4_133 v2_4_132)) check_unsat (and b4 (not (> (tupsort0_0 v2_4_133) (tupsort0_1 v2_4_133)))) (= e4_5 (and b4 b4)) (= b5 e4_5) === End function verification trace === === Begin function verification trace === (=> b1 (= v1_1_134 1)) (=> b1 (= v0_1_135 2)) check_unsat (not (distinct v0_1_135 v1_1_134)) === End function verification trace === |
Changes to tests/g0/verification/z3gen/e-z3gen-test-6.txt.
|
| < < < < < < < < < < < < < < < < | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | === Begin function verification trace === (=> b1 (= v0_1_113 p0)) (=> b1 (= v1_1_114 p1)) (= e1_2 (and b1 b1)) (= b2 e1_2) (=> e1_2 (= v0_2_115 v0_1_113)) (=> b2 (= v0_2_116 v0_2_115)) (=> e1_2 (= v1_2_117 v1_1_114)) (=> b2 (= v1_2_118 v1_2_117)) check_unsat (and b2 (not (and (= v0_2_116 33) (= v1_2_118 44)))) check_unsat (and b2 (not (or (= v0_2_116 55) (= v1_2_118 66)))) (= e2_3 (and b2 b2)) (= b3 e2_3) === End function verification trace === === Begin function verification trace === (=> b1 (= v1_1_119 2)) (=> b1 (= v0_1_120 1)) === End function verification trace === |
Changes to tests/g0/verification/z3gen/e-z3gen-test-bitwise.txt.
|
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | < | > | | | | | | | | > | | | | | > | | | | | | | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
=== Begin function verification trace ===
(=> b1 (= v0_1_113 p0))
(=> b1 (= v1_1_114 p1))
(=> b1 (= v2_1_115 p2))
(=> b1 (= v3_1_116 p3))
(=> b1 (= v4_1_118 u117))
(=> b1 (= v4_1_119 43981))
(=> b1 (= v5_1_121 u120))
(=> b1 (= v5_1_122 #x0000abcd))
(let ((a!1 (- (bv2int (bvor ((_ int2bv 32) v0_1_113) ((_ int2bv 32) v1_1_114)))
4294967296)))
(let ((a!2 (ite (bvslt (bvor ((_ int2bv 32) v0_1_113) ((_ int2bv 32) v1_1_114))
#x00000000)
a!1
(bv2int (bvor ((_ int2bv 32) v0_1_113) ((_ int2bv 32) v1_1_114))))))
(=> b1 (= v4_1_123 a!2))))
(=> b1 (= v5_1_124 (bvor v2_1_115 v3_1_116)))
(let ((a!1 (- (bv2int (bvand ((_ int2bv 32) v4_1_123) ((_ int2bv 32) v1_1_114)))
4294967296)))
(let ((a!2 (ite (bvslt (bvand ((_ int2bv 32) v4_1_123) ((_ int2bv 32) v1_1_114))
#x00000000)
a!1
(bv2int (bvand ((_ int2bv 32) v4_1_123)
((_ int2bv 32) v1_1_114))))))
(=> b1 (= v4_1_125 a!2))))
(=> b1 (= v5_1_126 (bvand v5_1_124 v3_1_116)))
(let ((a!1 (- (bv2int (bvxor ((_ int2bv 32) v4_1_125) ((_ int2bv 32) v1_1_114)))
4294967296)))
(let ((a!2 (ite (bvslt (bvxor ((_ int2bv 32) v4_1_125) ((_ int2bv 32) v1_1_114))
#x00000000)
a!1
(bv2int (bvxor ((_ int2bv 32) v4_1_125)
((_ int2bv 32) v1_1_114))))))
(=> b1 (= v4_1_127 a!2))))
(=> b1 (= v5_1_128 (bvxor v5_1_126 v3_1_116)))
check_unsat (not (bvult v2_1_115 #x00000020))
(let ((a!1 (- (bv2int (bvshl ((_ int2bv 32) v4_1_127) v2_1_115)) 4294967296)))
(let ((a!2 (ite (bvslt (bvshl ((_ int2bv 32) v4_1_127) v2_1_115) #x00000000)
a!1
(bv2int (bvshl ((_ int2bv 32) v4_1_127) v2_1_115)))))
(=> b1 (= v4_1_129 a!2))))
check_unsat (not (bvult v2_1_115 #x00000020))
(=> b1 (= v5_1_130 (bvshl v5_1_128 v2_1_115)))
check_unsat (not (bvult v2_1_115 #x00000020))
(let ((a!1 (- (bv2int (bvashr ((_ int2bv 32) v4_1_129) v2_1_115)) 4294967296)))
(let ((a!2 (ite (bvslt (bvashr ((_ int2bv 32) v4_1_129) v2_1_115) #x00000000)
a!1
(bv2int (bvashr ((_ int2bv 32) v4_1_129) v2_1_115)))))
(=> b1 (= v4_1_131 a!2))))
check_unsat (not (bvult v2_1_115 #x00000020))
(=> b1 (= v5_1_132 (bvlshr v5_1_130 v2_1_115)))
=== End function verification trace ===
=== Begin function verification trace ===
(=> b1 (= v3_1_133 #x00000000))
(=> b1 (= v2_1_134 #x00000000))
(=> b1 (= v1_1_135 0))
(=> b1 (= v0_1_136 0))
=== End function verification trace ===
|
Changes to tests/g0/verification/z3gen/e-z3gen-test-ghostfunc.txt.
|
| < < < < < < < < < < < < < < < < < < < < | | | | < > | | | < > | | | < > | | | | | | | | < > | | < > | | | < > | | | < > < > | | | < > | | < > | | | < > | | | < > < > | | | | | | < > | | | < > | | | | | | | | | | | < > | | < > | | | | | | < > | | | < > | | | | | | | | < > | > > | < < | | | | | | | < > < > | | | < > | | | | | | < > | | | < > < > | | < > | | | | | | | | | | | | | < > | | | | | | < > | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
=== Begin function verification trace ===
(=> b1 (= v0_1_113 p0))
(=> b1 (= v1_1_114 p1))
(=> b1 (= v2_1_116 u115))
(=> b1 (= v2_1_117 v0_1_113))
(= e1_2 (and b1 b1))
(= b2 e1_2)
(=> b2
(= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
118
1
2)
3))
(=> e1_2 (= v1_2_119 v1_1_114))
(=> b2 (= v1_2_120 v1_2_119))
(forall ((gca0_121 Int) (gca1_121 Int))
(! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
121
gca0_121
gca1_121)
(ite (and (= gca0_121 5) (= gca1_121 v1_2_120))
4
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
118
gca0_121
gca1_121)))))
(=> b2 a!1))
:weight 0))
(= e2_3 (and b2 b2))
(= b3 e2_3)
(=> e1_2 (= v2_2_122 v2_1_117))
(=> b2 (= v2_2_123 v2_2_122))
(=> e2_3 (= v2_3_124 v2_2_123))
(=> b3 (= v2_3_125 v2_3_124))
(= e3_4 (and b3 (distinct v2_3_125 5)))
(= b4 e3_4)
(= e4_5 (and b4 b4))
(= b5 e4_5)
(forall ((gca0_128 Int) (gca1_128 Int))
(! (=> e2_3
(= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
128
gca0_128
gca1_128)
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
121
gca0_128
gca1_128)))
:weight 0))
(forall ((gca0_129 Int) (gca1_129 Int))
(! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
129
gca0_129
gca1_129)
(ite (and (= gca0_129 1) (= gca1_129 2))
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
128
1
2)
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
128
gca0_129
gca1_129)))))
(=> b3 a!1))
:weight 0))
(forall ((gca0_127 Int) (gca1_127 Int))
(! (=> e3_4
(= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
127
gca0_127
gca1_127)
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
129
gca0_127
gca1_127)))
:weight 0))
(forall ((gca0_130 Int) (gca1_130 Int))
(! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
130
gca0_130
gca1_130)
(ite (and (= gca0_130 1) (= gca1_130 2))
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
127
1
2)
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
127
gca0_130
gca1_130)))))
(=> b4 a!1))
:weight 0))
(forall ((gca0_126 Int) (gca1_126 Int))
(! (=> e4_5
(= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
126
gca0_126
gca1_126)
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
130
gca0_126
gca1_126)))
:weight 0))
(forall ((gca0_131 Int) (gca1_131 Int))
(! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
131
gca0_131
gca1_131)
(ite (and (= gca0_131 1) (= gca1_131 2))
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
126
1
2)
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
126
gca0_131
gca1_131)))))
(=> b5 a!1))
:weight 0))
check_unsat (and b5
(not (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
126
1
2)
3)))
(= e5_6 (and b5 b5))
(= b6 e5_6)
(=> b6 (= v2_6_132 5))
(= e3_7 (and b3 (not (distinct v2_3_125 5))))
(= e6_7 (and b6 b6))
(= b7 (or e3_7 e6_7))
(= e7_8 (and b7 b7))
(= b8 e7_8)
(forall ((gca0_134 Int) (gca1_134 Int))
(! (=> e3_7
(= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
134
gca0_134
gca1_134)
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
129
gca0_134
gca1_134)))
:weight 0))
(forall ((gca0_135 Int) (gca1_135 Int))
(! (=> e5_6
(= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
135
gca0_135
gca1_135)
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
131
gca0_135
gca1_135)))
:weight 0))
(forall ((gca0_136 Int) (gca1_136 Int))
(! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
136
gca0_136
gca1_136)
(ite (and (= gca0_136 1) (= gca1_136 2))
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
135
1
2)
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
135
gca0_136
gca1_136)))))
(=> b6 a!1))
:weight 0))
(forall ((gca0_134 Int) (gca1_134 Int))
(! (=> e6_7
(= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
134
gca0_134
gca1_134)
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
136
gca0_134
gca1_134)))
:weight 0))
(forall ((gca0_137 Int) (gca1_137 Int))
(! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
137
gca0_137
gca1_137)
(ite (and (= gca0_137 1) (= gca1_137 2))
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
134
1
2)
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
134
gca0_137
gca1_137)))))
(=> b7 a!1))
:weight 0))
(forall ((gca0_133 Int) (gca1_133 Int))
(! (=> e7_8
(= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
133
gca0_133
gca1_133)
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
137
gca0_133
gca1_133)))
:weight 0))
(forall ((gca0_138 Int) (gca1_138 Int))
(! (let ((a!1 (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
138
gca0_138
gca1_138)
(ite (and (= gca0_138 1) (= gca1_138 2))
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
133
1
2)
(|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
133
gca0_138
gca1_138)))))
(=> b8 a!1))
:weight 0))
check_unsat (and b8
(not (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
133
1
2)
3)))
(=> e2_3 (= v1_3_139 v1_2_120))
(=> b3 (= v1_3_140 v1_3_139))
(=> e3_7 (= v1_7_141 v1_3_140))
(=> e3_4 (= v1_4_142 v1_3_140))
(=> b4 (= v1_4_143 v1_4_142))
(=> e4_5 (= v1_5_144 v1_4_143))
(=> b5 (= v1_5_145 v1_5_144))
(=> e5_6 (= v1_6_146 v1_5_145))
(=> b6 (= v1_6_147 v1_6_146))
(=> e6_7 (= v1_7_141 v1_6_147))
(=> b7 (= v1_7_148 v1_7_141))
(=> e7_8 (= v1_8_149 v1_7_148))
(=> b8 (= v1_8_150 v1_8_149))
check_unsat (and b8
(not (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
138
5
v1_8_150)
4)))
(=> e3_7 (= v2_7_151 v2_3_125))
(=> e6_7 (= v2_7_151 v2_6_132))
(=> b7 (= v2_7_152 v2_7_151))
(=> e7_8 (= v2_8_153 v2_7_152))
(=> b8 (= v2_8_154 v2_8_153))
check_unsat (and b8
(not (= (|_2_3s2:g0s3#20#sd:testGhostFunc_5Vo_5Vo_3ti1i0_6up_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1_2q_2_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1hs1:_n_5Vr_5Vo_5Vo_3ti1i0_5T_2RPpI_2i20i1h$3nPi0|
138
v2_8_154
v1_8_150)
4)))
(= e8_9 (and b8 b8))
(= b9 e8_9)
=== End function verification trace ===
=== Begin function verification trace ===
(=> b1 (= v1_1_155 2))
(=> b1 (= v0_1_156 1))
=== End function verification trace ===
|
Changes to tests/g0/verification/z3gen/e-z3gen-test-logic-and.txt.
|
| < < | | | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | === Begin function verification trace === (=> b1 (= v0_1_114 u113)) (=> b1 (= v0_1_115 false)) (=> b1 (= v1_1_117 u116)) (=> b1 (= v1_1_118 true)) (= e1_2 (and b1 v0_1_115)) (= b2 e1_2) (=> e1_2 (= v1_2_119 v1_1_118)) (=> b2 (= v1_2_120 v1_2_119)) (=> b2 (= v2_2_121 v1_2_120)) (= e1_3 (and b1 (not v0_1_115))) (= e2_3 (and b2 b2)) (= b3 (or e1_3 e2_3)) (=> e1_3 (= v122 false)) (=> e2_3 (= v2_3_123 v2_2_121)) (=> b3 (= v2_3_124 v2_3_123)) (=> e2_3 (= v122 v2_3_124)) (=> b3 (= v3_3_125 v122)) (=> b3 (= v4_3_127 u126)) (=> b3 (= v4_3_128 v3_3_125)) === End function verification trace === === Begin function verification trace === === End function verification trace === |
Changes to tests/g0/verification/z3gen/e-z3gen-test-logic-or.txt.
|
| < < | | | | | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | === Begin function verification trace === (=> b1 (= v0_1_114 u113)) (=> b1 (= v0_1_115 false)) (=> b1 (= v1_1_117 u116)) (=> b1 (= v1_1_118 true)) (= e1_2 (and b1 (not v0_1_115))) (= b2 e1_2) (=> e1_2 (= v1_2_119 v1_1_118)) (=> b2 (= v1_2_120 v1_2_119)) (=> b2 (= v2_2_121 v1_2_120)) (= e1_3 (and b1 v0_1_115)) (= e2_3 (and b2 b2)) (= b3 (or e1_3 e2_3)) (=> e1_3 (= v122 true)) (=> e2_3 (= v2_3_123 v2_2_121)) (=> b3 (= v2_3_124 v2_3_123)) (=> e2_3 (= v122 v2_3_124)) (=> b3 (= v3_3_125 v122)) (=> b3 (= v4_3_127 u126)) (=> b3 (= v4_3_128 v3_3_125)) === End function verification trace === === Begin function verification trace === === End function verification trace === |