Implements a disgusting method of funding which shader parameters turn out to be unused by the shader and emits warnings for them. Uses metal-objdump to disassemble the shader binary, finds a shader definition and looks through parameters. Parameters having "readnone" token are unused. Or so I think...
| 4268 | // Uses metal-objdump to disassemble the shader binary, finds a shader definition and looks through parameters. |
| 4269 | // Parameters having "readnone" token are unused. Or so I think... |
| 4270 | void DetectUnusedArguments( const char* libPath, ParserState& state, ASTNode* callNode ) |
| 4271 | { |
| 4272 | ZoneScoped; |
| 4273 | |
| 4274 | std::string entryName = ToString( callNode->GetChild( 1 )->GetSymbol()->name ); |
| 4275 | auto functionHeader = callNode->GetChild( 1 )->GetSymbol()->definition->GetChild( 0 ); |
| 4276 | |
| 4277 | for( auto child : functionHeader->GetChildren() ) |
| 4278 | { |
| 4279 | if( child && child->GetSymbol() ) |
| 4280 | { |
| 4281 | child->GetSymbol()->used = true; |
| 4282 | } |
| 4283 | } |
| 4284 | |
| 4285 | std::ostringstream cmd; |
| 4286 | cmd << MetalTool( "metal-objdump" ) << " -disassemble " << libPath; |
| 4287 | auto objdump = RunProcess( cmd.str().c_str() ); |
| 4288 | if( objdump.first != 0 ) |
| 4289 | { |
| 4290 | state.ShowMessage( callNode->GetLocation(), EC_CUSTOM_WARNING, "failed to run objdump, unused parameter information is unavailable" ); |
| 4291 | } |
| 4292 | |
| 4293 | size_t argumentIndex = 0; |
| 4294 | size_t stageInIndex = 0; |
| 4295 | |
| 4296 | std::regex shader( "define.*@" + entryName + "(.*)" ); |
| 4297 | std::smatch match; |
| 4298 | if( std::regex_search( objdump.second, match, shader ) ) |
| 4299 | { |
| 4300 | auto args = match[1].str(); |
| 4301 | SplitString( args, ',', [&]( auto arg ) { |
| 4302 | std::string argName = arg; |
| 4303 | Symbol* argumentSymbol = nullptr; |
| 4304 | |
| 4305 | if( argumentIndex < functionHeader->GetChildrenCount() ) |
| 4306 | { |
| 4307 | argumentSymbol = functionHeader->GetChild( argumentIndex )->GetSymbol(); |
| 4308 | |
| 4309 | bool isStageIn = false; |
| 4310 | for( auto reg : argumentSymbol->registerSpecifier ) |
| 4311 | { |
| 4312 | if( reg.second.registerType == MetalRegister::StageIn ) |
| 4313 | { |
| 4314 | isStageIn = true; |
| 4315 | break; |
| 4316 | } |
| 4317 | } |
| 4318 | |
| 4319 | argName = ToString( argumentSymbol->name ); |
| 4320 | if( isStageIn ) |
| 4321 | { |
| 4322 | argName += "." + ToString( functionHeader->GetChild( argumentIndex )->GetType().symbol->definition->GetChild( stageInIndex )->GetChild( 0 )->GetSymbol()->name ); |
| 4323 | if( ++stageInIndex >= functionHeader->GetChild( argumentIndex )->GetType().symbol->definition->GetChildrenCount() ) |
| 4324 | { |
| 4325 | ++argumentIndex; |
| 4326 | } |
| 4327 | } |
nothing calls this directly
no test coverage detected