Skip to content

Commit

Permalink
Merge pull request #1234 from zickgraf/CapJitTypedExpression
Browse files Browse the repository at this point in the history
Introduce CapJitTypedExpression
  • Loading branch information
zickgraf authored Dec 7, 2023
2 parents f701aff + af2ea35 commit 55ad6b4
Show file tree
Hide file tree
Showing 14 changed files with 329 additions and 45 deletions.
2 changes: 1 addition & 1 deletion CAP/PackageInfo.g
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ SetPackageInfo( rec(

PackageName := "CAP",
Subtitle := "Categories, Algorithms, Programming",
Version := "2023.12-04",
Version := "2023.12-05",
Date := (function ( ) if IsBound( GAPInfo.SystemEnvironment.GAP_PKG_RELEASE_DATE ) then return GAPInfo.SystemEnvironment.GAP_PKG_RELEASE_DATE; else return Concatenation( ~.Version{[ 1 .. 4 ]}, "-", ~.Version{[ 6, 7 ]}, "-01" ); fi; end)( ),
License := "GPL-2.0-or-later",

Expand Down
12 changes: 12 additions & 0 deletions CAP/gap/ToolsForCategories.gd
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,18 @@ DeclareGlobalFunction( "CapJitDataTypeOfMorphismOfCategory" );
DeclareGlobalFunction( "CapJitDataTypeOfTwoCellOfCategory" );
#! @EndGroup

#! @Description
#! (experimental) Simply returns <A>value</A>, but allows to specify the data type of <A>value</A> for CompilerForCAP.
#! <A>data_type_getter</A> must be a literal function or a global variable pointing to a function.
#! The function must accept no arguments or a single argument, and return a valid data type.
#! If the function accepts a single argument, it must be inside a CAP operation or method known to CompilerForCAP (for example, see <Ref Func="InstallMethodForCompilerForCAP" />),
#! and the current category (i.e. the first argument of the CAP operation or method known to CompilerForCAP) will be passed to the function.
#! IMPORTANT: If <A>data_type_getter</A> is a literal function, it must not contain references to variables in its context.
#! Otherwise the code might access random memory locations.
#! See <Ref BookName="CompilerForCAP" Func="CapJitInferredDataTypes" /> for more details on data types.
#! @Arguments value, data_type_getter
DeclareGlobalFunction( "CapJitTypedExpression" );

#! @Description
#! Computes a fixpoint of <A>func</A> with regard to equality given by <A>predicate</A>, starting with <A>initial_value</A>.
#! If no such fixpoint exists, the execution does not terminate.
Expand Down
3 changes: 3 additions & 0 deletions CAP/gap/ToolsForCategories.gi
Original file line number Diff line number Diff line change
Expand Up @@ -1333,6 +1333,9 @@ InstallGlobalFunction( CapJitDataTypeOfTwoCellOfCategory, function ( cat )

end );

##
InstallGlobalFunction( CapJitTypedExpression, ReturnFirst );

##
InstallGlobalFunction( CapFixpoint, function ( predicate, func, initial_value )
local x, y;
Expand Down
2 changes: 1 addition & 1 deletion CompilerForCAP/PackageInfo.g
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ SetPackageInfo( rec(

PackageName := "CompilerForCAP",
Subtitle := "Speed up and verify categorical algorithms",
Version := "2023.12-08",
Version := "2023.12-09",
Date := (function ( ) if IsBound( GAPInfo.SystemEnvironment.GAP_PKG_RELEASE_DATE ) then return GAPInfo.SystemEnvironment.GAP_PKG_RELEASE_DATE; else return Concatenation( ~.Version{[ 1 .. 4 ]}, "-", ~.Version{[ 6, 7 ]}, "-01" ); fi; end)( ),
License := "GPL-2.0-or-later",

Expand Down
34 changes: 33 additions & 1 deletion CompilerForCAP/gap/CompilerForCAP.gi
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,7 @@ InstallGlobalFunction( CapJitCompiledFunctionAsEnhancedSyntaxTree, function ( fu
domains := rec( );

pre_func := function ( tree, func_stack )
local value_of_binding_iterated, value_of_binding_and_CAP_JIT_INCOMPLETE_LOGIC_iterated, is_shorter_than, list_call, domain, simplify, enclosing_domain, index, resolved_domain, resolved_index, element;
local value_of_binding_iterated, value_of_binding_and_CAP_JIT_INCOMPLETE_LOGIC_iterated, is_shorter_than, list_call, domain, simplify, enclosing_domain, index, resolved_domain, resolved_index, element, element_type;

value_of_binding_iterated := function ( tree )
local func;
Expand Down Expand Up @@ -778,6 +778,38 @@ InstallGlobalFunction( CapJitCompiledFunctionAsEnhancedSyntaxTree, function ( fu
] ),
);

if IsBound( domain.data_type ) and IsBound( index.data_type ) then

element_type := CAP_JIT_INTERNAL_GET_OUTPUT_TYPE_OF_GLOBAL_FUNCTION_BY_INPUT_TYPES( "[]", [ domain.data_type, index.data_type ] );

if element_type = fail then

#Error( "could not determine element_type" );

elif IsFunction( element_type ) then

#Error( "cannot infer parametric output type by arguments types only" );

# COVERAGE_IGNORE_NEXT_LINE
else

element.data_type := element_type;

element.funcref.data_type := rec(
filter := IsFunction,
signature := Pair(
[
domain.data_type,
index.data_type
],
element_type
)
);

fi;

fi;

fi;

return rec(
Expand Down
87 changes: 83 additions & 4 deletions CompilerForCAP/gap/EnhancedSyntaxTree.gi
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,15 @@ BindGlobal( "CAP_JIT_INTERNAL_OPERATION_TO_SYNTAX_TREE_TRANSLATIONS", rec(
Assert( 0, Length( RecNames( CAP_JIT_INTERNAL_SYNTAX_TREE_TO_OPERATION_TRANSLATIONS ) ) = Length( RecNames( CAP_JIT_INTERNAL_OPERATION_TO_SYNTAX_TREE_TRANSLATIONS ) ) );

InstallGlobalFunction( ENHANCED_SYNTAX_TREE, function ( func )
local ErrorWithFuncLocation, globalize_hvars, only_if_CAP_JIT_RESOLVE_FUNCTION, given_arguments, type_signature, remove_depth_numbering, tree, orig_tree, pre_func, result_func, additional_arguments_func;
local WarnWithFuncLocation, ErrorWithFuncLocation, globalize_hvars, only_if_CAP_JIT_RESOLVE_FUNCTION, given_arguments, type_signature, remove_depth_numbering, tree, orig_tree, pre_func, result_func, additional_arguments_func;

WarnWithFuncLocation := function ( args... )

# using LocationFunc causes a segfault (https://github.com/gap-system/gap/issues/4507)
# COVERAGE_IGNORE_NEXT_LINE
CallFuncList( Print, Concatenation( [ "WARNING for function at ", TextAttr.b1, FilenameFunc( func ), ":", StartlineFunc( func ), TextAttr.reset, ":\n" ], args ) );

end;

ErrorWithFuncLocation := function ( args... )

Expand Down Expand Up @@ -110,7 +118,7 @@ InstallGlobalFunction( ENHANCED_SYNTAX_TREE, function ( func )
fi;

pre_func := function ( tree, additional_arguments )
local path, func_stack, new_tree, statements, i, statement, level, pos, lvars, value, to_delete, next_statement, funccall, translation, operation_name, can_possibly_be_resolved, info, case_expression, branch;
local path, func_stack, new_tree, statements, i, statement, level, pos, lvars, value, to_delete, next_statement, funccall, translation, operation_name, can_possibly_be_resolved, info, case_expression, data_type_getter, branch;

path := additional_arguments[1];
func_stack := additional_arguments[2];
Expand Down Expand Up @@ -142,6 +150,78 @@ InstallGlobalFunction( ENHANCED_SYNTAX_TREE, function ( func )

Assert( 0, IsBound( tree.type ) );

# handle CapJitTypedExpression, must be done at the beginning because we return the first argument
if StartsWith( tree.type, "EXPR_FUNCCALL_" ) and tree.funcref.type = "EXPR_REF_GVAR" and tree.funcref.gvar = "CapJitTypedExpression" then

if Length( tree.args ) <> 2 then

# COVERAGE_IGNORE_NEXT_LINE
ErrorWithFuncLocation( "CapJitTypedExpression must be called with exactly two arguments" );

fi;

if tree.args[2].type = "EXPR_FUNC" then

data_type_getter := SYNTAX_TREE_CODE( tree.args[2] );

elif tree.args[2].type = "EXPR_REF_GVAR" then

data_type_getter := ValueGlobal( tree.args[2].gvar );

else

# COVERAGE_IGNORE_NEXT_LINE
ErrorWithFuncLocation( "the second argument of CapJitTypedExpression must be a literal function or a reference to a global variable pointing to a function" );

fi;

tree := ShallowCopy( tree.args[1] );

if NumberArgumentsFunction( data_type_getter ) = 0 then

tree.data_type := data_type_getter( );

elif NumberArgumentsFunction( data_type_getter ) = 1 then

if Length( given_arguments ) = 0 or not IsCapCategory( given_arguments[1] ) then

# COVERAGE_IGNORE_NEXT_LINE
ErrorWithFuncLocation( "the data type getter of CapJitTypedExpression needs a category but we are not inside a CAP operation or a method known to CompilerForCAP with a CAP category as first argument" );

fi;

tree.data_type := data_type_getter( given_arguments[1] );

else

# COVERAGE_IGNORE_NEXT_LINE
ErrorWithFuncLocation( "the data type getter of CapJitTypedExpression must be a function accepting either no argument or a single argument" );

fi;

if IsFilter( tree.data_type ) then

tree.data_type := rec( filter := tree.data_type );

fi;

if not IsRecord( tree.data_type ) or not IsBound( tree.data_type.filter ) then

# COVERAGE_IGNORE_NEXT_LINE
ErrorWithFuncLocation( "CapJitTypedExpression has returned ", tree.data_type, " which is not a valid data type" );

fi;

fi;

# check for empty lists without type
if tree.type = "EXPR_LIST" and Length( tree.list ) = 0 and not IsBound( tree.data_type ) then

# COVERAGE_IGNORE_NEXT_LINE
WarnWithFuncLocation( "Found empty list without data type. Use CapJitTypedExpression for better compilation results.\n" );

fi;

# check for unsupported stuff
if tree.type = "STAT_EMPTY" then

Expand Down Expand Up @@ -544,9 +624,8 @@ InstallGlobalFunction( ENHANCED_SYNTAX_TREE, function ( func )

if not can_possibly_be_resolved then

# using LocationFunc causes a segfault (https://github.com/gap-system/gap/issues/4507)
# COVERAGE_IGNORE_NEXT_LINE
Print( "WARNING: operation ", tree.funcref.gvar, ", located in function at ", FilenameFunc( func ), ":", StartlineFunc( func ), " with name\n", NameFunction( func ), "\ncan probably not be resolved.\n" );
WarnWithFuncLocation( "operation ", tree.funcref.gvar, " can probably not be resolved.\n" );

fi;

Expand Down
51 changes: 34 additions & 17 deletions CompilerForCAP/gap/InferDataTypes.gi
Original file line number Diff line number Diff line change
Expand Up @@ -453,10 +453,14 @@ InstallGlobalFunction( CAP_JIT_INTERNAL_INFERRED_DATA_TYPES, function ( tree, in

if a.type = "EXPR_DECLARATIVE_FUNC" then

a := ShallowCopy( a );
if not IsBound( a.data_type ) then

# signature has to be set from somewhere else
a.data_type := rec( filter := IsFunction );
a := ShallowCopy( a );

# signature has to be set from somewhere else
a.data_type := rec( filter := IsFunction );

fi;

return a;

Expand Down Expand Up @@ -631,25 +635,38 @@ InstallGlobalFunction( CAP_JIT_INTERNAL_INFERRED_DATA_TYPES, function ( tree, in

if tree.list.length = 0 then

#Error( "cannot infer the type of empty lists" );
# there might already be a data type set, but we want to avoid partial typings -> unbind
Unbind( tree.data_type );
return tree;
if IsBound( tree.data_type ) then

data_type := tree.data_type;

else

# COVERAGE_IGNORE_BLOCK_START
PrintWithCurrentlyCompiledFunctionLocation( "WARNING: found empty list without data type. Make sure to use CapJitTypedExpression for manually written code and to properly set data types in logic functions creating empty lists." );

return tree;
# COVERAGE_IGNORE_BLOCK_END

fi;

fi;

if not ForAll( tree.list, element -> element.data_type = tree.list.1.data_type ) then
else

Display( "WARNING: list is not homogeneous, this is not supported. Use `NTuple` or its convenience aliases instead. The filters of the element types are:" );
DisplayWithCurrentlyCompiledFunctionLocation( List( AsListMut( tree.list ), element -> element.data_type.filter ) );
# there might already be a data type set, but we want to avoid partial typings -> unbind
Unbind( tree.data_type );
return tree;
if not ForAll( tree.list, element -> element.data_type = tree.list.1.data_type ) then

# COVERAGE_IGNORE_BLOCK_START
Display( "WARNING: list is not homogeneous, this is not supported. Use `NTuple` or its convenience aliases instead. The filters of the element types are:" );
DisplayWithCurrentlyCompiledFunctionLocation( List( AsListMut( tree.list ), element -> element.data_type.filter ) );
# there might already be a data type set, but we want to avoid partial typings -> unbind
Unbind( tree.data_type );
return tree;
# COVERAGE_IGNORE_BLOCK_END

fi;

data_type := CapJitDataTypeOfListOf( tree.list.1.data_type );

fi;

data_type := CapJitDataTypeOfListOf( tree.list.1.data_type );

elif tree.type = "EXPR_REF_GVAR" then

if IsBound( tree.data_type ) then
Expand Down
Loading

0 comments on commit 55ad6b4

Please sign in to comment.