Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add slangc flag to -zero-initialize all variables #3987

Open
wants to merge 59 commits into
base: master
Choose a base branch
from

Conversation

ArielG-NV
Copy link
Collaborator

@ArielG-NV ArielG-NV commented Apr 19, 2024

Adds -zero-initialize flag to set values to a __default() expression if they are missing an initExpr.
Fixes #3516

Adds `-zero-initialize` flag to set values to a __default() expression if they are missing a initExpr.
@ArielG-NV ArielG-NV changed the title Slangc flag to -zero-initialize all variables, #3516 Slangc flag to -zero-initialize all variables Apr 19, 2024
@ArielG-NV ArielG-NV changed the title Slangc flag to -zero-initialize all variables Add slangc flag to -zero-initialize all variables Apr 19, 2024
@@ -1636,6 +1636,23 @@ namespace Slang
}
}

if (!varDecl->initExpr &&
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this logic should be in SemanticsDeclBodyVisitor::checkVarDeclCommon instead.
There we are checking if a default ctor exists and calls it if so.

We should extend that logic by saying if default ctor does not exist, then initialize it with __default<T>().

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will not be-able todo this logic in BodyVisitor since then when setting init's to struct fields we would have already parsed+setup the default ctor body logic.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be synthesizing ctor signature in header visitor, and synthesize the ctor body in body visitor, there we call the ctors of the field. Why isn't this possible? We shouln't be checking the init exprs in body visitor IMO.

@csyonghe
Copy link
Collaborator

You should write
Fixes #xxxx
Or
Closes #xxxx
For GitHub to automatically link the issue.

1. We must keep zero-initialize in SemanticsDeclHeaderVisitor. This is done because else a ctor will be initialized before we can set struct fields to `__default`.
2. IRDefaultCtorDecoration was added to track default ctor's with parent struct.
3. ParentAggTypeModifier was added to track ChildOfStruct->IRType for sharing data such as with functions. This is required to ensure we associate a lowered function with a lowered struct type
This was done since decorations are checked for IR objects, storing auxillary info does not work here as a result if usable object.
@@ -1585,6 +1585,24 @@ class DynamicUniformModifier : public Modifier
SLANG_AST_CLASS(DynamicUniformModifier)
};

// Sharing IRInst data of AggType (Struct/Interface) with members.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be needed. In general we should never be creating AST nodes that references IR constructs.

IR_LEAF_ISA(StructType)

IRFunc* defaultCtor = nullptr;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't be adding fields to IR insts like this. All references must be encoded as either a child or an operand.

And IRStruct should not be referencing ctors directly. You can use a decoration to associate a default ctor if necessary. But i don't think we should need this, because we should be generating all default ctor calls during IR lowering, and we should never need to have the concept of a ctor in the IR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a hole in our generics system that we don't have to address in this PR.

What we probably should do now is that if T doesn't have a default initializer from any constraints, then we don't call any default ctor even after specialization. However, we still want to make sure we zero initialize whatever type it became after specialization with defaultConstruct inst.

So when we lower the generic function to IR, we check if T has default ctor, if it has we call it. If not we emit a defaultConstruct inst.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we do this, we don't need to keep track of the default ctor of each struct type in the IR in any explicit way.

Copy link
Collaborator Author

@ArielG-NV ArielG-NV Apr 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workaround added was not to handle generics, generics work as intended with __default as tested. I do see where this can be useful though (interfaces->struct to define an __init)

Passing a struct<->Ctor was done because of the cases like vector<...struct...>. We don't exactly know what is inside the container, so we cannot simply call an 'invoke' to the ctor. Only __default has emitting logic to manage these code edge cases which is why a ctor was passed through.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well,if you have:

struct MyContainer<T>
{
     T obj;
}

Then the right answer is always do obj = __default<T>(), which will eventually lower into defaultConstruct.

If you have:

struct MyContainer<T:IDefaultInitializable> // or any other interface that leads to `__init()` to be found
{
    T obj;
}

Then you lower it into:

obj = T();

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In either case, you shouldn't need to keep track of this additional ctor info.

{
for (auto ctor : decl->getMembersOfType<ConstructorDecl>())
{
auto parentStructTypeModifier = this->context->astBuilder->create<ParentStructTypeModifier>();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can simply maintain a side band dictionary in the ir lowering context to keep track of struct types and their ctors.

//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer
RWStructuredBuffer<int> outputBuffer;

__generic<T : __BuiltinArithmeticType>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For generics, we will need a IDefaultInitializable builtin interface that has a __init() requirement.

Then for any generic type that conforms to IDefaultInitializable, we can just call the interface method to initialize it. By default, we make all builtin types conform to this interface, and we can make all user defined struct types to automatically conform to the interface as we synthesize the default constructor.

Copy link
Collaborator Author

@ArielG-NV ArielG-NV Apr 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would solve the problem we have with vectors and remove the need for a workaround

we could define the vector IDefaultInitializable as the base type __default, which would resolve into a ctor eventually for all elements.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, all builtin interfaces should inherit from IDefaultInitializable.

@jkwak-work
Copy link
Collaborator

I am wondering if we can have tests for cases when "-zero-initialize" is not used.
In game development, the uninitialized state is preferred over implicit initialization with zero values for the runtime performance reason.
I am little worried that we may accidently initialize variables to zero even when "-zero-initialize" is not used.

@csyonghe
Copy link
Collaborator

@jkwak-work Agreed that we need a test case to cover this.

Basically, when -zero-initialize is not specified, then we should not be generating default ctors if a type does not have any member initializers, when we should not be generating assignments in default ctors for members that are not initialized.

Then, if we don't synthesis such a default ctor, we won't be calling the default ctor at variable declaration sites.

@ArielG-NV
Copy link
Collaborator Author

I will add the test case, I agree that there should be one for each possible senario:

  1. default init generated due to field init expression
  2. no init should be generated due to 0 fields with init expressions

@ArielG-NV ArielG-NV changed the title Add slangc flag to -zero-initialize all variables WIP: Add slangc flag to -zero-initialize all variables Apr 22, 2024
Since `IDefaultInitializable` is taking a considerabley larger amount of time than anticipated I am pushing some of the other fixes requested. I did not remove the "IRStruct storing a default Ctor" hack yet.

mostly renamed/adjusted tests to work as intended

added test to ensure we don't synthisize a junk `= 0` when not in `zero initialize` mode

removed member in favor of sharedContext+dictionary.
@@ -1639,7 +1639,8 @@ namespace Slang
}
}

// We must keep zero-initialize in SemanticsDeclHeaderVisitor.
// We must keep zero-initialize for struct fields in
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure I understand the comment here.

@@ -1636,6 +1636,23 @@ namespace Slang
}
}

if (!varDecl->initExpr &&
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be synthesizing ctor signature in header visitor, and synthesize the ctor body in body visitor, there we call the ctors of the field. Why isn't this possible? We shouln't be checking the init exprs in body visitor IMO.

@@ -498,6 +498,9 @@ struct SharedIRGenContext
Dictionary<Stmt*, IRBlock*> breakLabels;
Dictionary<Stmt*, IRBlock*> continueLabels;

// A reference manager to a ctor's parent
Dictionary<FunctionDeclBase*, IRType*> ctorBaseToParentStruct;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be able to get this with findResultTypeForConstructorDecl?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SemanticsVisitor logic is disjoint from IRGenContext and is not something which can work-without a fully visited SemanticsVisitor?

if (parentStructTypeModifier && decl->members.getCount() == 0)
as<IRStructType>(parentStructTypeModifier->getArg())->defaultCtor = as<IRFunc>(lowered.val);
IRType** maybeParentStruct = this->context->shared->ctorBaseToParentStruct.tryGetValue(decl);
if (maybeParentStruct && decl->members.getCount() == 0)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think this is needed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(reference to previous comment - #3987 (comment))

1. IDefaultInitializer interface was added. If conforming, your type may be zero-initialized. To Conform a `__init()` is required
2. `[OnlyAutoInitIfForced]` was added. This attribute states that a default initializer should only be implicitly called if forced by the compiler (`zero-initialize` for example). This allows types which implicitly/explicitly conform to IDefaultInitialize to have optional auto-init behavior (which is Slang's default for user structs) to be disabled.

* note about `[OnlyAutoInitIfForced]`. This is required for std-lib to not automatically resolve init-expressions for std-lib, but it has the added benifit of allowing user made structs/classes to control the default behavior of initializing
source/slang/slang-check-conformance.cpp Outdated Show resolved Hide resolved
source/slang/slang-check-conformance.cpp Outdated Show resolved Hide resolved
@@ -1948,8 +1948,47 @@ namespace Slang
checkVisibility(classDecl);
}

static Expr* constructDefaultInitExprForVar(SemanticsVisitor* visitor, VarDeclBase* varDecl)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it make sense to be mayConstruct....?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the following name was suggested instead of maybe... because if this function doesn't construct it is due to invalid types to avoid a language server crash.
We generally do not add maybe if a language server crash is being avoided.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what you meant here. Why is function name related to a crash. What is language server?

Copy link
Collaborator Author

@ArielG-NV ArielG-NV May 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I will add maybe... for simplicity if you think it would help the code. If someone else disagrees it will be an easy change

  2. To answer the other questions

What is language server?

Having no type is an error with Slang. Slangc will not lead to a position like this.

Slangc though is not the only way to use Slang, Slang has a language server. This is how the "Slang vscode extension" works.

The language server does not mirror how a regular compile works. Due to this, Slang code needs to be robust enough to avoid a crash if invalid data is given to a function. This is possible because Slang's language server works on incomplete, likely invalid input. A crash is an issue because then the code will not be able to send the 'errors' to the calling application.

Why is function name related to a crash

All code to avoid a language server failure are not addressed in function name from what I have seen.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have never heard the term, "language server".
Do we have a better description or document about it somewhere?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you said about the language server, you were talking about a crash with slang extension for visual studio?

When you said, "Slang has a language server", i got an impression that we implemented something called a language server. But it seems like you meant to say, "slang uses a language server," is it the case?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ArielG-NV , can you answer to my questions on my previous comment?

Copy link
Collaborator Author

@ArielG-NV ArielG-NV May 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I did not see the comment.
slangc is a compiler and a language server, the logic for the language server 'is the compiler'.

source\compiler-core\slang-language-server-protocol.cpp

  • implements the protocol for use when slangc is compiled+used as a language server

DiagnosticSink::Flag::LanguageServer

  • diagnose issues from the slang language server in a format useful to a language server protocol (LSP)

Because of this, Slang code needs to add additional checks to avoid failures specifically because a crash during diagnosis of incorrect code is fatal to a language server. A regular compile is different because we would still send a error for the user to interface with.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting...
How do I test whether or not my change didn't break the language server?
Does slang-test have a test coverage on that?
Or do I need to install something more and run other tests?

source/slang/slang-check-decl.cpp Show resolved Hide resolved
source/slang/slang-check-decl.cpp Show resolved Hide resolved
Comment on lines 482 to 486
[ForceInline]
__init()
{
this = __default<ThisType>();
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that cpp files checks the option like this,

this->getOptionSet().getBoolOption(CompilerOptionName::ZeroInitialize)

and change the behavior based on the command-line argument.
But this line doesn't check the option and it will always initialize the variables with zero, isn't it?
If it is the case, I am worried that Slang generated code will pay the cost of initialization even when they shouldn't be initialized.

If my understanding is incorrect, please let me know how it works.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stdlib structs do not set init expressions if they have an empty initExpr.
This is due to the following code having !isFromStdLib to ensure we don't default init (.*).meta.slang types unless CompilerOptionName::ZeroInitialize is enabled.

SemanticsDeclBodyVisitor::checkVarDeclCommon
else if(!varDeclBaseType || !varDeclBaseType->getDeclRef() || !isFromStdLib(varDeclBaseType->getDeclRef().getDecl()))

I don't think this affects raytracing types, but it seems it does not (falcor works)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I am following your explanation but it raised another question.
When slangc runs for the first time, it compiles stdlib and it is cached. And next time we run slangc, we don't recompile stdlib.

From this point, the run time compiler option will have no impact on how stdlib gets compiled, because the compilation doesn't happen anymore.

Does the implementation consider this case?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The part I don't understand is that you said,

stdlib structs do not set init expressions if they have an empty initExpr.

But it looks like you are setting initExpr here.

this = __default<ThisType>();

I think "__default()" is initExpr, isn't it?

Copy link
Collaborator Author

@ArielG-NV ArielG-NV May 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the implementation consider this case?

This has 2 parts:

  1. does IDefaultInitialize get added to meta.slang

stdlib will not automatically add IDefaultInitialize if compiled without.
This cannot be worked around since a user does not have the ability to compile (.).meta.slang

  1. does it still work as expected

Yes. To simplify the answer I will use vec2 as an example: if we write int myVar with the -zero-initialize flag, the following code will compile int myVar = 0.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me change my question.
What does this line do?

this = __default<ThisType>();

Copy link
Collaborator Author

@ArielG-NV ArielG-NV May 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

int a = __default<ThisType>(); => int a = 0;
float a = __default<ThisType>(); => float a = 0.0f;
etc...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it will initialized to zero even when -zero-initialize is not set, is it the case?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it will initialized to zero even when -zero-initialize is not set, is it the case?

no, the init is not automatically called. It just exists for us to use.
Image if I make an interface thing{ __init(); } --> this interface would not work with int, which is incorrect.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After talking on Slack for a bit, it turned out that @ArielG-NV was on an impression that we shouldn't be calling the constructors if the struct/class is defined in stdlib.

He thought that it is our design choice because he misinterpreted what Yong said on one of comments,

We probably want to exclude stdlib from this behaivor, though.

I don't believe that is what we want at all but I like to hear from @csyonghe if my understanding is correct.

source/slang/slang-check-impl.h Outdated Show resolved Hide resolved
@@ -51,19 +51,23 @@ namespace Slang

SubtypeWitness* SemanticsVisitor::isSubtype(
Type* subType,
Type* superType)
Type* superType,
bool subtypeInheritanceIsNotFullyResolved
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like to suggest to refactor the new boolean variable.
It appears that the behavior around the new boolean variable is that

  • when it is true, "isSubtype()" behaves same as before.
  • when it is false, "isSubtype()" behaves little differently.

If my observation is correct, you could make a new function like following without changing the existing function, "isSubtype()",

SubtypeWitness * XXX:isSubtypeWhenInheritanceIsNotFullyResolved(Type* subType, Type* superType)
{
    auto result = isSubtype(subType, superType);
    if (result == null_ptr)
    {
       do something here...
    }
    return result;
}

The reason why I suggest this is that "boolean" parameter is not a good thing to have in general.
It can be only true or false, which can be same as having two distinct functions instead of having a single function with a boolean parameter.
For more information, I like to suggest a very good book called, "Clean code", written by Martin C Robert.
His another book, "Agile Software Development, Principles, Patterns, and Practices", is also a fantastic book to checkout.

source/slang/slang-options.cpp Outdated Show resolved Hide resolved
@ArielG-NV
Copy link
Collaborator Author

I like to suggest to refactor the new boolean variable.

Likely best for future refactor, agreed.

I checked on the CI and realized I caused a failure, mistake was made not negating some code
@ArielG-NV
Copy link
Collaborator Author

Update: jkwak-work found a flaw with how std types were treated. This is being addressed.

@jkwak-work
Copy link
Collaborator

Update: jkwak-work found a flaw with how std types were treated. This is being addressed.

Can you push the change so that I can see how it is changed?

@ArielG-NV
Copy link
Collaborator Author

Update: jkwak-work found a flaw with how std types were treated. This is being addressed.

Can you push the change so that I can see how it is changed?

pushed the changes

previously defaultConstruct emitting was crashing due to having generics unresolved. By not resolving the default construct immediately, everything works.
@jkwak-work
Copy link
Collaborator

jkwak-work commented May 24, 2024

With this change, if I don't use "-zero-initialize" option, the results are expected to be same as before.
And I tested it locally and saw that one of tests we have shows difference.

tests/language-feature/struct-field-initializers/struct-field-initializer-static.slang

Can you see why the result is changed with your code, and make sure everything works as expected?

The test is not failing but SPIR-V code is different when I compared the outcome with the following command.

slangc -target spirv-asm -o out.spirv tests/language-feature/struct-field-initializers/struct-field-initializer-static.slang

@jkwak-work
Copy link
Collaborator

When I generated GLSL of the test, I see that "data1" is incorrectly unused with your code.
It seems like there is a bug in your implementation.

@ArielG-NV
Copy link
Collaborator Author

ArielG-NV commented May 24, 2024

With this change, if I don't use "-zero-initialize" option, the results are expected to be same as before. And I tested it locally and saw that one of tests we have shows difference.

tests/language-feature/struct-field-initializers/struct-field-initializer-static.slang

Can you see why the result is changed with your code, and make sure everything works as expected?

The test is not failing but SPIR-V code is different when I compared the outcome with the following command.

slangc -target spirv-asm -o out.spirv tests/language-feature/struct-field-initializers/struct-field-initializer-static.slang

Took a bit to check because I wanted to get a clean master first*

There are differences. These of which I can explain, I will share the shaders for ease of reading:

### --> zero-init branch
#version 450
layout(row_major) uniform;
layout(row_major) buffer;

layout(std430, binding = 0) buffer StructuredBuffer_int_t_0 {
    int _data[];
} outputBuffer_0;

struct DefaultStructNoInit_base_0
{
    int data2_0;
};

DefaultStructNoInit_base_0 DefaultStructNoInit_base_x24init_0()
{
    DefaultStructNoInit_base_0 _S1;
    _S1.data2_0 = 2;
    return _S1;
}

struct DefaultStructNoInit_0
{
    DefaultStructNoInit_base_0 base_0;
    int data3_0;
};

DefaultStructNoInit_0 DefaultStructNoInit_x24init_0()
{
    DefaultStructNoInit_0 _S2;
    _S2.base_0 = DefaultStructNoInit_base_x24init_0();
    _S2.data3_0 = 2;
    return _S2;
}

int data1_0;

layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void main()
{
    data1_0 = 2;
    DefaultStructNoInit_0 noInit_0 = DefaultStructNoInit_x24init_0();
    bool _S3;
    if(noInit_0.base_0.data2_0 == 2)
    {
        _S3 = noInit_0.data3_0 == 2;
    }
    else
    {
        _S3 = false;
    }
    outputBuffer_0._data[0U] = int(_S3);
    return;
}

### --> master
#version 450
layout(row_major) uniform;
layout(row_major) buffer;

layout(std430, binding = 0) buffer StructuredBuffer_int_t_0 {
    int _data[];
} outputBuffer_0;

int data1_0;

struct DefaultStructNoInit_base_0
{
    int data2_0;
};

DefaultStructNoInit_base_0 DefaultStructNoInit_base_x24init_0()
{
    data1_0 = 2;
    DefaultStructNoInit_base_0 _S1;
    _S1.data2_0 = 2;
    return _S1;
}

struct DefaultStructNoInit_0
{
    DefaultStructNoInit_base_0 base_0;
    int data3_0;
};

DefaultStructNoInit_0 DefaultStructNoInit_x24init_0()
{
    DefaultStructNoInit_0 _S2;
    DefaultStructNoInit_base_0 _S3 = DefaultStructNoInit_base_x24init_0();
    _S2.base_0 = _S3;
    _S2.data3_0 = 2;
    return _S2;
}

layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
void main()
{
    data1_0 = 2;
    DefaultStructNoInit_0 noInit_0 = DefaultStructNoInit_x24init_0();
    bool _S4;
    if(data1_0 == 2)
    {
        _S4 = noInit_0.base_0.data2_0 == 2;
    }
    else
    {
        _S4 = false;
    }
    if(_S4)
    {
        _S4 = noInit_0.data3_0 == 2;
    }
    else
    {
        _S4 = false;
    }

    outputBuffer_0._data[0U] = int(_S4);
    return;
}

@ArielG-NV
Copy link
Collaborator Author

ArielG-NV commented May 24, 2024

Differences;

  1. master: static int data1 = 2; init's in the __init() of a function and at entrypoint; zero-init branch: Now, only entry point sets static variables (correct behavior)

  2. data1 is now placed lower in program code (since referenced first at a later date)

@ArielG-NV
Copy link
Collaborator Author

ArielG-NV commented May 24, 2024

why this happens:
Since we run ensureDecl(struct.member, DeclCheckState::DefaultConstructorReadyForUse), now the code sets up all modifiers+checks+inits correctly before we run bodyVisitor->visitAggTypeDecl().

why this was added to this PR instead of it's own:
It was necessary to fix this to implement zero-initialize.

@ArielG-NV
Copy link
Collaborator Author

ArielG-NV commented May 24, 2024

When I generated GLSL of the test, I see that "data1" is incorrectly unused with your code. It seems like there is a bug in your implementation.

This is correct behavior, master branch does things incorrectly currently.
Notice data_1 is initialized by DefaultStructNoInit_base_x24init_0(), this is wrong, data1 is a static and should only be initialized at the start of main()

@jkwak-work
Copy link
Collaborator

jkwak-work commented May 24, 2024

I think your explanation makes sense.
I was aware that you fixed a bug in this PR.
I didn't know it could be related to an existing test.
Good job.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Option to zero-initialize all variables
3 participants