hi Chris! thanks for your reply. First of all I did not know about the history with the Annotation stuff. Annotable for me was a way how one could realize this things. So as I see it right now - it is more that Annotable will completly vanish soon. This is interesting to me. Chris Lattner schrieb:> On Fri, 24 Feb 2006, Jakob Praher wrote:> >> When translating a complex c application to llvm bytecodes, some >> semantics are lost: >> > LLVM 1.6 and the "new front-end" already handle this right. Here's the > bugzilla bug corresponding to it: > http://llvm.cs.uiuc.edu/bugs/show_bug.cgi?id=659Great! The bug information is rather scarce. I would be interested, how you implemented it. Did you add another bytecode entry for the section value mapping? Is it possible to add attributes to other elements like functions as well? Did you think about a mapping of common attributes on different platforms. For instance DLLMain Entry point under Win32 and the __attribute__((constructor)) under Linux.> >> >> I would generally be interested and could contribute to extending LLVM >> by allowing more Annotatinons than currently possible.Okay so I am on quite the opposite attitude than the LLVM team towards that issue :-)> > At one point in time, Value was annotatable. The problem with this was > two fold: > > 1. This bloat every value in the system, by adding an extra pointer. > 2. These annotations would get stale and not be updated correctly. > > The problem is basically that adding annotations really amounts to > extending the LLVM IR, and making it look like something simple doesn't > make it easier to deal with. For example, if you add an "I'm special" > attribute to an instruction, then the function is cloned by some pass, > is that attribute copied or not? What if it is deleted, moved, > rearranged, etc? Further, how can annotations be serialized to .ll > files and .bc files? In llvm, we always want "opt -pass1 -pass2" to be > the same as "opt -pass1 | opt -pass2", which would break if annotations > can't be serialized (which they can't currently). >I get you 100 % here. But as you say later in the mail, many information is done by some runtime std::map<Value*,foo> stuff. Which is really handy at runtime, but I *had* serialization in mind when I was thinking about Annotations. I see annotations as a way to serialize some extra information with the bytecode without having to extend/change the core classes. The best way to implemented in runtime is to use some kind of std::map subscripting, plus the additional benefit that you can serialize it to the bytecode. Perhaps the best of both worlds. Two things here: (1) Annotations should not be something which really changes the meaning of a Value/Type. All the passes should work without the annotation. (2) I think annotations are a handy way to augment the bytecode without changing the bytecode format. It gives people the freedom to add some extra information. This is also interesting since changing the bytecode/adding fields to Value/... is often not a real option since one wants to work with production core libraries. (like I do now). Perhaps the thing could be solved by adding policy statemetns to annotations. I could imagine the inventor of an Annotation should think about how the annotation should behave during optimisation/change. So the anntation should have a policy field which defaults to DontCare. In that case the user of the Annotation cannot be sure that it will get retained or something like that. Given the discussion that happens in the higher level vms (like Gilad Bracha's Paper on pluggable type systems) gives some hints about the difficulties in changing Instruction Sets over time. I think core system functionality is invariant, but meta information, that is not essential for the application to work should be pluggable too.> As a historical curiosity, Function still needs to be annotatable due to > the LLVM code generator relying on it. This will be fixed in LLVM 1.8 > and Function will not be annotable anymore. > > If you *really* just want per-pass local data, you should just use an > std::map from the Value* to your data.Why not see Annotations as the means to serialize these Maps. Maybe we could add an Annotations table that maps Value types to ConstantPool entries or something like that. This would make it more easily for LLVM libraries in other languages too.> >> %struct.A = type { int } >> %struct.B = type { int } >> >> BTW: How would one generate a type alias like the above through the LLVM >> API? > > > Add two entries to the module symbol table for the same Type using > Module::addTypeName.Very interesting. I then have to take the type by calling Module::getTypeByName to have a second Type pointer or? since I saw the llvm-gcc generates code like: %pa = alloca %struct.A %pb = alloca %struct.B this means that the AllocaInst must have knowledge about two types which can only be so by having two different pointers? right?> >> But sometimes it would be interesting to actually get symbol information >> about a type beeing used, without the need for full featured debug >> information ala DWARF. > > > This isn't something you can do, this is far more tricky than you make > it out to be. :) >Hehe, I know. Certainly if someone like you says that. But *if* the front end is aware of the annotation, which would be doable, and the annations are serializable in bytecode, then one would have this information during LLVM bytecode processing as well. One could also emit the symbolic information like Relocation informations in a .section or as I am currently working with the JIT - use the JITs information about the annotations to get the symbolic information.>> This could be also solved by introducing Annotable. >> For instance if the alloca/malloc/.. instruction would get an Anntation >> about a symbolic type which could look like: >> >> { ("x",int) } >> >> One could use the DEF/USE and operand information in the byte code to >> know which symbolic field was accessed for instance through >> getelementptr. > > > Again, this is effectively extending the LLVM IR. Calling it an > 'annotation' doesn't make it simpler. :) Also, the front-end would have > to be modified to generate the annotation.See above. Every information must be understood in order to be usable. But I would do it as an annotation since it is just additional meta information and the program would perfectly run without the information.> >> I don't know how you feel about that, but I there would be many >> circumstances where Annotations could help getting more information out >> of the bytecode. > > > While I understand the general utility of annotations, the LLVM > Annotation facility has several problems (some of which are described > above) that make them not work well in practice. Even if they did, they > would still have the "updating" class of problems, which I'm not sure > how to solve. > > If you think that this is something that would be really useful, you can > come up with solutions for these issues, and you're willing to implement > it, then this is the right place to talk about the design of the new > facility. :)Hehe. Yes. I am just getting comfortable with the framework and I think it is very nice. If I have some more points (which I hope I have) I will definitely talk to you. -- Jakob> > -Chris >
Hi Jakob, I have some thoughts on this too .. On Fri, 2006-02-24 at 19:56 +0100, Jakob Praher wrote:> I get you 100 % here. But as you say later in the mail, many information > is done by some runtime std::map<Value*,foo> stuff. Which is really > handy at runtime, but I *had* serialization in mind when I was thinking > about Annotations. I see annotations as a way to serialize some extra > information with the bytecode without having to extend/change the core > classes. The best way to implemented in runtime is to use some kind of > std::map subscripting, plus the additional benefit that you can > serialize it to the bytecode. Perhaps the best of both worlds. > > Two things here: > (1) Annotations should not be something which really changes the meaning > of a Value/Type. All the passes should work without the annotation. > > (2) I think annotations are a handy way to augment the bytecode without > changing the bytecode format. It gives people the freedom to add some > extra information. This is also interesting since changing the > bytecode/adding fields to Value/... is often not a real option since one > wants to work with production core libraries. (like I do now). > > Perhaps the thing could be solved by adding policy statemetns to > annotations. I could imagine the inventor of an Annotation should think > about how the annotation should behave during optimisation/change. So > the anntation should have a policy field which defaults to DontCare. In > that case the user of the Annotation cannot be sure that it will get > retained or something like that. > > Given the discussion that happens in the higher level vms (like Gilad > Bracha's Paper on pluggable type systems) gives some hints about the > difficulties in changing Instruction Sets over time. I think core system > functionality is invariant, but meta information, that is not essential > for the application to work should be pluggable too.As Chris mentioned, I would prefer that we keep annotations out of the core IR altogether as they are fraught with problems that are not easy to resolve. However, I understand where you're coming from in wanting to keep additional information with the bytecode. I have wanted the same thing for use by front end or specialized tools. For example an IDE that could keep track of source information or a language that needs special passes that can only be done at link time. In thinking about the "right" way to do this, I came up with the idea of a single "blob" of data that could be appended to a Module. This single "annotation" would always be ignored by LLVM, would not require significant additional space to construct, and there is already a mechanism for constructing the information via the bytecode reader's handler interface (might need some extension). This is simply a way of making that std::map of information embeddable in the bytecode. It means the information is stored in one additional bytecode block (at the end) where it doesn't have any impact on LLVM (JIT/storage/etc). The only question is: how do multiple tools avoid collision in this approach. Some kind of registry or partitioning of the data could likely solve that.> > > As a historical curiosity, Function still needs to be annotatable due to > > the LLVM code generator relying on it. This will be fixed in LLVM 1.8 > > and Function will not be annotable anymore. > > > > If you *really* just want per-pass local data, you should just use an > > std::map from the Value* to your data. > > Why not see Annotations as the means to serialize these Maps. Maybe we > could add an Annotations table that maps Value types to ConstantPool > entries or something like that. This would make it more easily for LLVM > libraries in other languages too.This is similar to my idea above, but I wouldn't want to restrict it to any particular data structure. The application can construct the data however it wishes and simply pass a pointer to a block of memory to the bytecode writer. Reid -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 189 bytes Desc: This is a digitally signed message part URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20060224/66b99f11/attachment.sig>
Hi Reid, Reid Spencer schrieb:> I have some thoughts on this too .. >Great!> On Fri, 2006-02-24 at 19:56 +0100, Jakob Praher wrote: > >>I get you 100 % here. But as you say later in the mail, many information >>is done by some runtime std::map<Value*,foo> stuff. Which is really >>handy at runtime, but I *had* serialization in mind when I was thinking >>about Annotations. I see annotations as a way to serialize some extra >>information with the bytecode without having to extend/change the core >>classes. The best way to implemented in runtime is to use some kind of >>std::map subscripting, plus the additional benefit that you can >>serialize it to the bytecode. Perhaps the best of both worlds. >>...> > As Chris mentioned, I would prefer that we keep annotations out of the > core IR altogether as they are fraught with problems that are not easy > to resolve. However, I understand where you're coming from in wanting to > keep additional information with the bytecode. I have wanted the same > thing for use by front end or specialized tools. For example an IDE that > could keep track of source information or a language that needs special > passes that can only be done at link time. >Yes.> In thinking about the "right" way to do this, I came up with the idea of > a single "blob" of data that could be appended to a Module. This single > "annotation" would always be ignored by LLVM, would not require > significant additional space to construct, and there is already a > mechanism for constructing the information via the bytecode reader's > handler interface (might need some extension). >As far as locality is concerned, perhaps it would make sense to make such a blob on every primary object (module,function), so that annotations that only apply to a certain function can be stored directly in the function. That would make certain collisions easier to resolve.> This is simply a way of making that std::map of information embeddable > in the bytecode. It means the information is stored in one additional > bytecode block (at the end) where it doesn't have any impact on LLVM > (JIT/storage/etc). The only question is: how do multiple tools avoid > collision in this approach. Some kind of registry or partitioning of the > data could likely solve that. >Yes that sounds like a doable approach. But I would not write any binary data into the blob, but use a LLVM type encoding approach/table approach. Many annotations are simple or can be composite simple types and people should be encouraged to store data in a way, that makes it possible to read it without library code. If you just serialize C++ structs, you end up relying heavy on the code that wrote it. Which makes it harder for tools to introspect anntoations. Java's annotations rely on simple types for the same principle and I think it is the right way for most things. There could be an opaque type for more complex information, which should be discouraged. This would also make it possible to have tripple of Value,AnnotationType,Name to match the Annotation, which helps to the solve the collision problem too. The lookup mechanism could lookup by anything of the tripple: - Target Value - AnnotationType - Name NULL values are wildcards. So you could say: Give me all annotations for a Value* /// Function local annotations Value* v = ... vector< const Annotation *> &ans = curFunction->lookupAnnotation( v, NULL, NULL); Or based on a specific type: /// Module wide annoations AnnotationType *type = ... Value< const Annotation *> &ans = module->lookupAnnotation( v, type, NULL ); This just random thought though.> >>>As a historical curiosity, Function still needs to be annotatable due to >>>the LLVM code generator relying on it. This will be fixed in LLVM 1.8 >>>and Function will not be annotable anymore. >>> >>>If you *really* just want per-pass local data, you should just use an >>>std::map from the Value* to your data. >> >>Why not see Annotations as the means to serialize these Maps. Maybe we >>could add an Annotations table that maps Value types to ConstantPool >>entries or something like that. This would make it more easily for LLVM >>libraries in other languages too. > > > This is similar to my idea above, but I wouldn't want to restrict it to > any particular data structure. The application can construct the data > however it wishes and simply pass a pointer to a block of memory to the > bytecode writer. >Great that we have a similar view. I would use a public simple type encoding for the annotations, So that annotations are introspectable without knowing much on the details of the annotation data. This helps to keep the bytecode free from language specific data encoding too. -- Jakob> > ------------------------------------------------------------------------ > > _______________________________________________ > LLVM Developers mailing list > LLVMdev at cs.uiuc.edu http://llvm.cs.uiuc.edu > http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
> thanks for your reply.Sorry for the delay, I've been buried in email lately.>>> When translating a complex c application to llvm bytecodes, some >>> semantics are lost: >>> >> LLVM 1.6 and the "new front-end" already handle this right. Here's the >> bugzilla bug corresponding to it: >> http://llvm.cs.uiuc.edu/bugs/show_bug.cgi?id=659 > > Great! The bug information is rather scarce. I would be interested, how > you implemented it. Did you add another bytecode entry for the section > value mapping? Is it possible to add attributes to other elements like > functions as well?Yes, it was added to the .ll/.bc formats: http://llvm.cs.uiuc.edu/docs/LangRef.html#globalvars http://llvm.cs.uiuc.edu/docs/BytecodeFormat.html#globalinfo> Did you think about a mapping of common attributes on different > platforms. For instance DLLMain Entry point under Win32 and the > __attribute__((constructor)) under Linux.__attribute__((constructor)) is handled with a the llvm.globalctors global variable (even with llvm 1.6), try it out.>>> I would generally be interested and could contribute to extending LLVM >>> by allowing more Annotatinons than currently possible.> Okay so I am on quite the opposite attitude than the LLVM team towards > that issue :-)I don't follow.>> At one point in time, Value was annotatable. The problem with this was >> two fold: >> >> 1. This bloat every value in the system, by adding an extra pointer. >> 2. These annotations would get stale and not be updated correctly. >> >> The problem is basically that adding annotations really amounts to >> extending the LLVM IR, and making it look like something simple doesn't >> make it easier to deal with. For example, if you add an "I'm special" >> attribute to an instruction, then the function is cloned by some pass, >> is that attribute copied or not? What if it is deleted, moved, >> rearranged, etc? Further, how can annotations be serialized to .ll >> files and .bc files? In llvm, we always want "opt -pass1 -pass2" to be >> the same as "opt -pass1 | opt -pass2", which would break if annotations >> can't be serialized (which they can't currently). >> > > I get you 100 % here. But as you say later in the mail, many information > is done by some runtime std::map<Value*,foo> stuff. Which is really > handy at runtime, but I *had* serialization in mind when I was thinking > about Annotations.Okay, if you want to serialize/deserialize, they become much more palatable, the implementation just gets stickier.> I see annotations as a way to serialize some extra > information with the bytecode without having to extend/change the core > classes. The best way to implemented in runtime is to use some kind of > std::map subscripting, plus the additional benefit that you can > serialize it to the bytecode. Perhaps the best of both worlds.That's fine, but don't think that makes them solve all of the problems. Again, there is still the updating issue.> Two things here: > (1) Annotations should not be something which really changes the meaning > of a Value/Type. All the passes should work without the annotation.Okay, what use are they then? Note that source language types are not unique in LLVM, and they shouldn't be even with annotations. For example: struct X { int A; }; struct Y { int B; }; Both X and Y map to the same LLVM Type. This cannot change.> (2) I think annotations are a handy way to augment the bytecode without > changing the bytecode format. It gives people the freedom to add some > extra information. This is also interesting since changing the > bytecode/adding fields to Value/... is often not a real option since one > wants to work with production core libraries. (like I do now).Okay.> Perhaps the thing could be solved by adding policy statemetns to > annotations. I could imagine the inventor of an Annotation should think > about how the annotation should behave during optimisation/change. So > the anntation should have a policy field which defaults to DontCare. In > that case the user of the Annotation cannot be sure that it will get > retained or something like that.Personally, I see annotations as a convenient way to do experiments and allow rapid development. If we decide that a feature makes sense in the LLVM IR long term, it should be added as a first class feature of it.>>> %struct.A = type { int } >>> %struct.B = type { int } >>> >>> BTW: How would one generate a type alias like the above through the LLVM >>> API? >> >> >> Add two entries to the module symbol table for the same Type using >> Module::addTypeName. > > Very interesting. I then have to take the type by calling > Module::getTypeByName to have a second Type pointer or?Again, see above, there is no way to distinguish between two source level types that have the same structure.> since I saw the llvm-gcc generates code like: > > %pa = alloca %struct.A > %pb = alloca %struct.B > > this means that the AllocaInst must have knowledge about two types which > can only be so by having two different pointers? right?This is an implementation detail of the old llvm-gcc that breaks with the new one. Do not depend on it.>>> But sometimes it would be interesting to actually get symbol information >>> about a type beeing used, without the need for full featured debug >>> information ala DWARF.>> This isn't something you can do, this is far more tricky than you make >> it out to be. :)> Hehe, I know. Certainly if someone like you says that. But *if* the > front end is aware of the annotation, which would be doable, and the > annations are serializable in bytecode, then one would have this > information during LLVM bytecode processing as well.Yes. However, there would be no way to keep isomorphic LLVM types separate. This dramatically limits the usefulness of what you're trying to do.> One could also emit the symbolic information like Relocation > informations in a .section or as I am currently working with the JIT - > use the JITs information about the annotations to get the symbolic > information.I don't understand.>>> This could be also solved by introducing Annotable. >>> For instance if the alloca/malloc/.. instruction would get an Anntation >>> about a symbolic type which could look like: >>> >>> { ("x",int) } >>> >>> One could use the DEF/USE and operand information in the byte code to >>> know which symbolic field was accessed for instance through >>> getelementptr. >> >> >> Again, this is effectively extending the LLVM IR. Calling it an >> 'annotation' doesn't make it simpler. :) Also, the front-end would have >> to be modified to generate the annotation. > > See above. Every information must be understood in order to be usable. > But I would do it as an annotation since it is just additional meta > information and the program would perfectly run without the information.Hopefully I made the issue more clear above. -Chris -- http://nondot.org/sabre/ http://llvm.org/
On Fri, 24 Feb 2006, Reid Spencer wrote:> In thinking about the "right" way to do this, I came up with the idea of > a single "blob" of data that could be appended to a Module. This single > "annotation" would always be ignored by LLVM, would not require > significant additional space to construct,If you're talking about a blob of binary data, which does not have "pointers" into the LLVM code, this is fine. However, there is no reason it needs to be stored "in" the BC format. It can live "next" to it in the same file without impacting the bc format.> and there is already a > mechanism for constructing the information via the bytecode reader's > handler interface (might need some extension).Not clear. If you're having "pointers" from the blob into the LLVM code (which I believe you would need for it to be useful), then the compiler code that manipulates the LLVM IR has to be aware of these pointers, must be able to update them when it makes changes, etc. This is extremely nontrivial: exactly the same problem as full fledged annotations. Additionally, the LLVM linker would have to know how to merge these blobs of data (simple concatenation would be fine, more complex things probably not).> (JIT/storage/etc). The only question is: how do multiple tools avoid > collision in this approach. Some kind of registry or partitioning of the > data could likely solve that.Also an issue. -Chris -- http://nondot.org/sabre/ http://llvm.org/
hi, Chris Lattner schrieb:>> thanks for your reply. > > Yes, it was added to the .ll/.bc formats: > http://llvm.cs.uiuc.edu/docs/LangRef.html#globalvars > http://llvm.cs.uiuc.edu/docs/BytecodeFormat.html#globalinfoInteresting. I will check it out.> >> Did you think about a mapping of common attributes on different >> platforms. For instance DLLMain Entry point under Win32 and the >> __attribute__((constructor)) under Linux. > > > __attribute__((constructor)) is handled with a the llvm.globalctors > global variable (even with llvm 1.6), try it out. >Great!>> Okay so I am on quite the opposite attitude than the LLVM team towards >> that issue :-) > > > I don't follow. >All I wanted to say here is that while I thought Function was the first Value beeing annotatable, it turned out to remain the last :-)>>> At one point in time, Value was annotatable. The problem with this was >>> two fold: >>> >>> 1. This bloat every value in the system, by adding an extra pointer. >>> 2. These annotations would get stale and not be updated correctly. >>> >>> The problem is basically that adding annotations really amounts to >>> extending the LLVM IR, and making it look like something simple doesn't >>> make it easier to deal with. For example, if you add an "I'm special" >>> attribute to an instruction, then the function is cloned by some pass, >>> is that attribute copied or not? What if it is deleted, moved, >>> rearranged, etc? Further, how can annotations be serialized to .ll >>> files and .bc files? In llvm, we always want "opt -pass1 -pass2" to be >>> the same as "opt -pass1 | opt -pass2", which would break if annotations >>> can't be serialized (which they can't currently). >>> >> >> I get you 100 % here. But as you say later in the mail, many information >> is done by some runtime std::map<Value*,foo> stuff. Which is really >> handy at runtime, but I *had* serialization in mind when I was thinking >> about Annotations. > > Okay, if you want to serialize/deserialize, they become much more > palatable, the implementation just gets stickier.Hmm not sure I understand you here. What I don't want is that the spec and the implementation intermix. I think if there is a serialization in use it should have a well known format. That have to be easy to beeing operated on with small tools. It should be made of primitve types that can be composed (like the Struct Type for instance). For instnace an annotation value could refer to a Typeslot, which means Annotions uses the LLVM types.> >> I see annotations as a way to serialize some extra >> information with the bytecode without having to extend/change the core >> classes. The best way to implemented in runtime is to use some kind of >> std::map subscripting, plus the additional benefit that you can >> serialize it to the bytecode. Perhaps the best of both worlds. > > > That's fine, but don't think that makes them solve all of the problems. > Again, there is still the updating issue. >Hmm. I have not too much experience here. So I think you are right in this regard. See below. Perhaps the update issue is really in the domain of the annotation writer. If the person does not think about it, then the combination of two annotations should be left out. I think one could implement them using a callback. But see below.>> Two things here: >> (1) Annotations should not be something which really changes the meaning >> of a Value/Type. All the passes should work without the annotation. > > > Okay, what use are they then? >I think the difference is mission critical versus usable. I think metadata of this kind is very usable, but should not stop non-aware passes from running. So it is optional. I put it in wrong way obove. Values should get augmented by more metadata, but not change the way the other passes work.> Note that source language types are not unique in LLVM, and they > shouldn't be even with annotations. For example: > > struct X { int A; }; > struct Y { int B; }; > > Both X and Y map to the same LLVM Type. This cannot change.This is alright. And I am aware of that. LLVM has a structural equivalent type system. And I think it is the right in terms of LIR. But you can for instance tag the alloca instruction with an annotation, which adds symbolic type information. Since the alloca binds the type to a stack location this would be an option. (and other allocation/getelementptr instruction as well).> >> Perhaps the thing could be solved by adding policy statemetns to >> annotations. I could imagine the inventor of an Annotation should think >> about how the annotation should behave during optimisation/change. So >> the anntation should have a policy field which defaults to DontCare. In >> that case the user of the Annotation cannot be sure that it will get >> retained or something like that. > > > Personally, I see annotations as a convenient way to do experiments and > allow rapid development. If we decide that a feature makes sense in the > LLVM IR long term, it should be added as a first class feature of it. >Hmm. In my view most of the annotations should be just metadata. Like the example above. But if it turns out that an annotation is more than this, and valueable this would be the right way to do.>> since I saw the llvm-gcc generates code like: >> >> %pa = alloca %struct.A >> %pb = alloca %struct.B >> >> this means that the AllocaInst must have knowledge about two types which >> can only be so by having two different pointers? right? > > > This is an implementation detail of the old llvm-gcc that breaks with > the new one. Do not depend on it. >Okay. Thanks for the info.> >> Hehe, I know. Certainly if someone like you says that. But *if* the >> front end is aware of the annotation, which would be doable, and the >> annations are serializable in bytecode, then one would have this >> information during LLVM bytecode processing as well. > > > Yes. However, there would be no way to keep isomorphic LLVM types > separate. This dramatically limits the usefulness of what you're trying > to do. >Maybe I was not clear. I want to attach extra informfation at use time of variables for instance. This information is optional. So I don't divide isomorphic LLVM types. Every type is bound to a variable through a special instruction. If you annotate these instructions you can always find the symbolic type of a such a variable. This is just one use of annotations. It clearly does not change the meaning of instructions, but only attach meta information. For later use: You can then use the information, for instance: -> in the JIT (easy) -> during code generation, you can add symbol information to memory address much like the relocation entries in ELF. So you can use the address of the variable to get its symbolic type.>> >> >> See above. Every information must be understood in order to be usable. >> But I would do it as an annotation since it is just additional meta >> information and the program would perfectly run without the information. > > > Hopefully I made the issue more clear above. >Hope so too. Please tell me what you think. But I could see many uses of simple meta annotations.> -Chris >