? Still having problems with compiler modification ?

44 views
Skip to first unread message

Jimmy Johnson

unread,
Mar 8, 2015, 11:02:49 PM3/8/15
to eiffelst...@googlegroups.com

I have made some progress.  I can compile the compiler and use that modified version of the compiler to compile a test system.  The program runs, but my injected procedure does not get called after the assignment statement as I expected.

I added a feature to ANY which I want called after an assignment statement:

class
ANY
feature -- Customization
persistence_manager
-- Test calling the procedure
do
print ("This is a call from my feature in ANY. %N")
end

And in class AST_FEATURE_CHECKER_GENERATOR.process_assign_as in the if statement as approximately line 5780 I added code, trying to follow the previous advice:

if l_error_level = error_handler.error_level and is_byte_node_enabled then
-- Only create BYTE code if there is no error.
create l_assign
l_assign.set_target (l_target_node)
l_assign.set_line_number (l_as.target.start_location.line)
l_source_expr ?= last_byte_node
l_assign.set_source (l_source_expr)
l_assign.set_line_pragma (l_as.line_pragma)
last_byte_node := l_assign
-----------------------------------------------------------------------------------------------
-- Code added by jjj
c_class := context.current_class

feature_i := c_class.feature_named ("persistence_manager")
feature_b := feature_i.access_for_feature (none_type, Void, False, False)
create byte_list.make (2)
byte_list.extend (l_assign) -- from above
byte_list.extend (feature_b) -- from here
create instr_list_b.make (byte_list)
last_byte_node := instr_list_b
-- end jjj addition
-----------------------------------------------------------------------------------------------
end
end
if attached l_target_type and then l_reinitialized_variable /= 0 then

Why does the procedure not execute?
thanks,
jjj

Alexander Kogtenkov

unread,
Mar 9, 2015, 6:57:01 AM3/9/15
to eiffelst...@googlegroups.com
Did you look at the generated melted and C code? Is it different from regular compiler code?

If it is different, for some reason the execution does not go the way you like to.

If it is the same, it makes sense to look at code generation under debugger. In melted mode you can put a conditional breakpoint on {MELTED_GENERATOR}.process_instr_list_b, in C mode - on {INSTR_LIST_B}.generate. It's better to use a precompiled library to avoid massive breaks at those breakpoints and a minimal test example to see why it does not work as expected. Also the condition can be used to narrow down the number of breaks even futher, checking on context.current_feature and context.associated_class.

As a side note, would you like to document your experience in extending compiler code at dev.eiffel.com for other people who may want to do similar things, describing what you needed and what should be done, step-by-step, to achieve it?

Regards,

Alexander Kogtenkov

--
For more messaging options, visit this group at http://forum.eiffel.com.
Information on the Eiffelstudio project: http://dev.eiffel.com.

Jimmy Johnson

unread,
Mar 9, 2015, 4:48:52 PM3/9/15
to eiffelst...@googlegroups.com


On Monday, March 9, 2015 at 6:57:01 AM UTC-4, Alexander Kogtenkov wrote:
Did you look at the generated melted and C code? Is it different from regular compiler code?

Yes, it is different.
There is a new declaration:  "EIF_REFERENCE tr3 = NULL" and an a new RTLR (...) call.
Also, this seems odd [to my limited understanding]:  In the ISE-compiler generated call there is a sequence:
   tr1 = ((up1x = (FUNCTION_CAST (...
   RTHOOK(3);
which seems reversed in the version generated from my compiler:
   RTHOOK(3);
   tr1 = ((up1x = (FUNCTION_CAST (...

Question:  In the code [in previous post] should I add a new node at the end of a BYTE_LIST with `extend' or at the beginning?  (I.e. is my previous code okay there?)

If it is different, for some reason the execution does not go the way you like to.

 That is true.  Executing the system compiled with my compiler causes "Illigal instruction: 4". 
 
If it is the same, it makes sense to look at code generation under debugger. In melted mode you can put a conditional breakpoint on {MELTED_GENERATOR}.process_instr_list_b, in C mode - on {INSTR_LIST_B}.generate. It's better to use a precompiled library to avoid massive breaks at those breakpoints and a minimal test example to see why it does not work as expected. Also the condition can be used to narrow down the number of breaks even futher, checking on context.current_feature and context.associated_class.

Not really able to debug in Xcode with a project using a make file, but in EiffelStudio feature MELTED_GENERATOR.process_instr_list_b never gets executed.
 
In my code I am adding a FEATURE_B to the BYTE_LIST.  I assume this is a construct for a "feature".  Shouldn't I add a construct for a "feature call" instead?  Perhaps a CALL_B?  

If so, how do I construct a CALL_B? 


As a side note, would you like to document your experience in extending compiler code at dev.eiffel.com for other people who may want to do similar things, describing what you needed and what should be done, step-by-step, to achieve it?

Perhaps I can take some notes as I go along, and if I get some free time I can look in to doing that.
Thanks,
jjj

Emmanuel Stapf

unread,
Mar 9, 2015, 5:07:19 PM3/9/15
to eiffelst...@googlegroups.com

The best thing to do is to start with a one single line of Eiffel code and compare the whole body of the routine between the two versions. It should guide you. The fact it crashes shows that something does not get initialized properly. Maybe you are calling a routine without providing the Current object as argument, or something along those lines.

 

Note that the RTHOOK(3) corresponds to the 3rd breakpoint in the flatview of the routine.

 

Regards,

Manu

Jimmy Johnson

unread,
Mar 9, 2015, 5:53:45 PM3/9/15
to eiffelst...@googlegroups.com, ma...@eiffel.com

On Monday, March 9, 2015 at 5:07:19 PM UTC-4, Emmanuel Stapf wrote:

The best thing to do is to start with a one single line of Eiffel code and compare the whole body of the routine between the two versions. It should guide you. The fact it crashes shows that something does

I manually added the call to the feature (defined in the "customization" section of ANY) and I see a RTWF.  (Which is a feature [procedure] call, right?)
 (FUNCTION_CAST(void, (EIF_REFERENCE)) RTWF(3342, Dtype(Current)))(Current);

Shouldn't my code contain a similar line if my compiler was inserting a call to that feature?

 

not get initialized properly. Maybe you are calling a routine without providing the Current object as argument, or something along those lines.

1) I don't think I am even calling anything, because I am not inserting a call; I'm inserting a feature.  (I think.)

feature_i := c_class.feature_named ("persistence_manager")
feature_b := feature_i.access_for_feature (none_type, Void, False, False)
create byte_list.make (2)
byte_list.extend (l_assign) -- from above
byte_list.extend (feature_b) -- from here
create instr_list_b.make (byte_list)
last_byte_node := instr_list_b

The "feature_b" local is not really a FEATURE_B; it is an ACCESS_B, because the return type of feature_i.access_for_feature does not conform to FEATURE_B.


2)  How do I create an object representing a feature call?
3)  How do I initialize it?  (We can assume the feature call is defined in ANY.)
4)  How do I initialize the one argument to be the target of the assignment that is being process?

 

Note that the RTHOOK(3) corresponds to the 3rd breakpoint in the flatview of the routine.

I see, thanks.
jjj

Alexander Kogtenkov

unread,
Mar 10, 2015, 8:26:02 AM3/10/15
to eiffelst...@googlegroups.com, Emmanuel Stapf
> There is a new declaration:  "EIF_REFERENCE tr3 = NULL" and an a new RTLR (...) call.

Those are for initialization, but what about a feature call?


> Question:  In the code [in previous post] should I add a new node at the end of a BYTE_LIST with `extend' or at the beginning?  (I.e. is my previous code okay there?)

`extend' should be fine.

> MELTED_GENERATOR.process_instr_list_b never gets executed.

It should be executed only if you perform melt operation, not freeze.


> I manually added the call to the feature (defined in the "customization" section of ANY) and I see a RTWF.  (Which is a feature [procedure] call, right?)
>     (FUNCTION_CAST(void, (EIF_REFERENCE)) RTWF(3342, Dtype(Current)))(Current);
>
> Shouldn't my code contain a similar line if my compiler was inserting a call to that feature?

Yes, it should have such code.


> The "feature_b" local is not really a FEATURE_B; it is an ACCESS_B, because the return type of feature_i.access_for_feature does not conform to FEATURE_B.

This is OK.


> 2)  How do I create an object representing a feature call?

ACCESS_B is a descendant of CALL_B, so everything seems to be OK. If we were talking about a feature call used as an instruction,  it could be wrapped in INSTR_CALL_B as done in process_instr_call_as. You can try that to see if it helps.

Alexander Kogtenkov

Jimmy Johnson

unread,
Mar 10, 2015, 10:32:47 PM3/10/15
to eiffelst...@googlegroups.com, ma...@eiffel.com
On Tuesday, March 10, 2015 at 8:26:02 AM UTC-4, Alexander Kogtenkov wrote:
> There is a new declaration:  "EIF_REFERENCE tr3 = NULL" and an a new RTLR (...) call.

Those are for initialization, but what about a feature call?

When my compiler executes it puts in this line:


tr1 = ((up1x = (FUNCTION_CAST(EIF_TYPED_VALUE, (EIF_REFERENCE)) RTWF(7, dtype))(Current)), (((up1x.type & SK_HEAD) == SK_REF)? (EIF_REFERENCE) 0: (up1x.it_r = RTBU(up1x))), (up1x.type = SK_POINTER), up1x.it_r);


which is not in the code if ISE's compiler is used; so, something is happening. 


> MELTED_GENERATOR.process_instr_list_b never gets executed.

It should be executed only if you perform melt operation, not freeze.

I'm compiling from scratch.  If MELTED_GENERATOR.process_instr_list_b  is not processing my list (that I added) then where is my list being processed?  or is it?  There is an assignment statement "s := "Does the assignment..." that is placed in the list and it seems to get processed.

loc1 = RTMS_EX_H("Does the assignment get processed\? \012",36,632077066);

tr1 = ((up1x = (FUNCTION_CAST(EIF_TYPED_VALUE, (EIF_REFERENCE)) RTWF(7, dtype))(Current)), (((up1x.type & SK_HEAD) == SK_REF)? (EIF_REFERENCE) 0: (up1x.it_r = RTBU(up1x))), (up1x.type = SK_POINTER), up1x.it_r);


What is RTMS_EX_H?  Where is it defined?
 
> 2)  How do I create an object representing a feature call?

ACCESS_B is a descendant of CALL_B, so everything seems to be OK. If we were talking about a feature call used as an instruction,  it could be wrapped in INSTR_CALL_B as done in process_instr_call_as. You can try that to see if it helps.

I could not make this work.


I suspect this modification is not too hard for someone that understands the compiler.  I am just not getting it.  How much will it cost for one of you Eiffel compiler experts to just tell me how to do this?

Original code:
    do
        s := "Does the assignment get processed? %N"
    end
I want the compiler to process as if it the code were:
    do
         s := "Does the assignment get processed? %N"
         my_procedure (s)          -- from customized  ANY
    end

I do appreciate you've taken on this question.
Best regards,
jjj

Alexander Kogtenkov

unread,
Mar 11, 2015, 3:34:11 AM3/11/15
to eiffelst...@googlegroups.com, Emmanuel Stapf
It's a bit difficult to follow all the possibilities you try, as sometimes the feature in ANY is a procedure without arguments, in the example in the last mail it is a procedure with one argument, and in the generated code it looks like a call to a function, because it returns a value.

> I'm compiling from scratch.  If MELTED_GENERATOR.process_instr_list_b  is not processing my list (that I added) then where is my list being processed?

There are at least 2 modes of compilation from scratch: melted and frozen. In the first case you need to use a precompiled library, do not have any external features in your code and do not request to freeze. If any of these conditions is false the code gets frozen and MELTED_GENERATOR is not used at all. So far from what you write I can guess that you always freeze the code, therefore MELTED_GENERATOR.process_instr_list_b is not called.

>  How much will it cost for one of you Eiffel compiler experts to just tell me how to do this?

I believe we are in the situation similar to the one discussed earlier:

>> As a side note, would you like to document your experience in extending compiler code at dev.eiffel.com for other people who may want to do similar things, describing what you needed and what should be done, step-by-step, to achieve it?
>Perhaps I can take some notes as I go along, and if I get some free time I can look in to doing that.

Would it work if you send your version of ANY and I'll prepare a patch that calls the feature you want to after every assignment?

Alexander Kogtenkov

--

Jimmy Johnson

unread,
Mar 11, 2015, 1:19:56 PM3/11/15
to eiffelst...@googlegroups.com, ma...@eiffel.com
On Wednesday, March 11, 2015 at 3:34:11 AM UTC-4, Alexander Kogtenkov wrote:
It's a bit difficult to follow all the possibilities you try, as sometimes the feature in ANY is a procedure without arguments, in the example in the last mail it is a procedure with one argument, and in the generated code it looks like a call to a function, because it returns a value.

Sorry for the confusion.
The generated code is meaningless, as that is the problem.
I was trying to simplify as much as possible.  I have just been trying to call a procedure without arguments, but since I was begging for help anyway, I figured I would see if you could show how to set the argument, where the argument is the target of the previous assignment statement.


Would it work if you send your version of ANY and I'll prepare a patch that calls the feature you want to after every assignment?

Alexander Kogtenkov

 The version of ANY I have been trying to use simply contains a stub:

feature -- Customization
persistence_manager
-- Test calling the procedure
do
print ("This is a call from my feature in ANY. %N")
end

The real version would replace the print statement with "(create {PERSISTENCE_FACILITIES}).some_feature (the_argument)".

1.  The `persistence_manager' feature should take an argument of type ANY.
2.  Maybe it should not be in ANY.  I figured that was easier than trying to call PERSISTENCE_FACILITIES.some_feature.
3.  As mentioned, `some_feature' should only be called if the assignment is to an attribute of the class.  (I think you told me how to do that in a previous post.)  Furthermore, if possible, if the target is a basic type or SPECIAL [basic type] then call another procedure with Current as the argument.  (But this is above and beyond right now.)

There are other modifications I need to make which I'm hopping can be modeled after the solution to this problem.  (I'm sure I will have more question when I have to make a new AST_NODE.)

Thanks so much for your help and continued interest,
Jimmy J. Johnson  (boxer41a   at   yahoo  dot  com  if you want to take this off line.)

Alexander Kogtenkov

unread,
Mar 13, 2015, 6:58:44 AM3/13/15
to eiffelst...@googlegroups.com
Attached is a patch that supports

   persistence_manager
      do
         ...
      end

and

   persistence_manager (x: detachable ANY)
      do
         ...
      end

in a current class (possibly inheriting the procedure from ANY). The procedure is called only if there is an assignment to an attribute of a reference type. In the latter case the value of the attribute is passed as an argument to `persistence_manager' if the second version of the declaration is used. So, in case you do not want to generate those calls all the time, `persistence_manager' can be put to a dedicated class that can be inherited only when special processing is required. Also, the passed attribute value is checked for conformance to the type of the argument of the procedure. Thus changing the type of the argument from "detachable ANY" to some other type one can make sure only attributes with a conforming type are passed to the procedure.

Alexander Kogtenkov

--
persistence_manager.patch

Jimmy Johnson

unread,
Mar 16, 2015, 6:40:25 PM3/16/15
to eiffelst...@googlegroups.com
Alexander,
Thanks so much for the patch.  I would never have figured that out.  I learned a lot from it, but my idea will not work.  It recurses infinitely because even the simplest function call, such as "print (...)" eventually calls a procedure that itself has an assignment.
So back to the drawing board.
jjj

Reply all
Reply to author
Forward
0 new messages