From be967e31fc7e839d6152b5c703631c538e7f1d1c Mon Sep 17 00:00:00 2001 From: dan sinclair Date: Thu, 16 Feb 2023 19:22:19 +0000 Subject: [PATCH] Add JSON output to tint_info. This CL adds a `--json` output to the `tint_info` command to generate a JSON file of the WGSL program information. Change-Id: I8cdd9675954f793fc7142d857f846a18f4a07ffc Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/120082 Reviewed-by: Ben Clayton Kokoro: Kokoro Commit-Queue: Dan Sinclair --- src/tint/cmd/helper.cc | 314 +++++++++++++++++++++++++---------------- src/tint/cmd/helper.h | 40 ++++++ src/tint/cmd/info.cc | 278 ++++++++++++++++++++++++++++++++---- 3 files changed, 483 insertions(+), 149 deletions(-) diff --git a/src/tint/cmd/helper.cc b/src/tint/cmd/helper.cc index 702050eda6..f3760dad16 100644 --- a/src/tint/cmd/helper.cc +++ b/src/tint/cmd/helper.cc @@ -20,125 +20,6 @@ namespace tint::cmd { namespace { -std::string TextureDimensionToString(tint::inspector::ResourceBinding::TextureDimension dim) { - switch (dim) { - case tint::inspector::ResourceBinding::TextureDimension::kNone: - return "None"; - case tint::inspector::ResourceBinding::TextureDimension::k1d: - return "1d"; - case tint::inspector::ResourceBinding::TextureDimension::k2d: - return "2d"; - case tint::inspector::ResourceBinding::TextureDimension::k2dArray: - return "2dArray"; - case tint::inspector::ResourceBinding::TextureDimension::k3d: - return "3d"; - case tint::inspector::ResourceBinding::TextureDimension::kCube: - return "Cube"; - case tint::inspector::ResourceBinding::TextureDimension::kCubeArray: - return "CubeArray"; - } - - return "Unknown"; -} - -std::string SampledKindToString(tint::inspector::ResourceBinding::SampledKind kind) { - switch (kind) { - case tint::inspector::ResourceBinding::SampledKind::kFloat: - return "Float"; - case tint::inspector::ResourceBinding::SampledKind::kUInt: - return "UInt"; - case tint::inspector::ResourceBinding::SampledKind::kSInt: - return "SInt"; - case tint::inspector::ResourceBinding::SampledKind::kUnknown: - break; - } - - return "Unknown"; -} - -std::string TexelFormatToString(tint::inspector::ResourceBinding::TexelFormat format) { - switch (format) { - case tint::inspector::ResourceBinding::TexelFormat::kR32Uint: - return "R32Uint"; - case tint::inspector::ResourceBinding::TexelFormat::kR32Sint: - return "R32Sint"; - case tint::inspector::ResourceBinding::TexelFormat::kR32Float: - return "R32Float"; - case tint::inspector::ResourceBinding::TexelFormat::kBgra8Unorm: - return "Bgra8Unorm"; - case tint::inspector::ResourceBinding::TexelFormat::kRgba8Unorm: - return "Rgba8Unorm"; - case tint::inspector::ResourceBinding::TexelFormat::kRgba8Snorm: - return "Rgba8Snorm"; - case tint::inspector::ResourceBinding::TexelFormat::kRgba8Uint: - return "Rgba8Uint"; - case tint::inspector::ResourceBinding::TexelFormat::kRgba8Sint: - return "Rgba8Sint"; - case tint::inspector::ResourceBinding::TexelFormat::kRg32Uint: - return "Rg32Uint"; - case tint::inspector::ResourceBinding::TexelFormat::kRg32Sint: - return "Rg32Sint"; - case tint::inspector::ResourceBinding::TexelFormat::kRg32Float: - return "Rg32Float"; - case tint::inspector::ResourceBinding::TexelFormat::kRgba16Uint: - return "Rgba16Uint"; - case tint::inspector::ResourceBinding::TexelFormat::kRgba16Sint: - return "Rgba16Sint"; - case tint::inspector::ResourceBinding::TexelFormat::kRgba16Float: - return "Rgba16Float"; - case tint::inspector::ResourceBinding::TexelFormat::kRgba32Uint: - return "Rgba32Uint"; - case tint::inspector::ResourceBinding::TexelFormat::kRgba32Sint: - return "Rgba32Sint"; - case tint::inspector::ResourceBinding::TexelFormat::kRgba32Float: - return "Rgba32Float"; - case tint::inspector::ResourceBinding::TexelFormat::kNone: - return "None"; - } - return "Unknown"; -} - -std::string ResourceTypeToString(tint::inspector::ResourceBinding::ResourceType type) { - switch (type) { - case tint::inspector::ResourceBinding::ResourceType::kUniformBuffer: - return "UniformBuffer"; - case tint::inspector::ResourceBinding::ResourceType::kStorageBuffer: - return "StorageBuffer"; - case tint::inspector::ResourceBinding::ResourceType::kReadOnlyStorageBuffer: - return "ReadOnlyStorageBuffer"; - case tint::inspector::ResourceBinding::ResourceType::kSampler: - return "Sampler"; - case tint::inspector::ResourceBinding::ResourceType::kComparisonSampler: - return "ComparisonSampler"; - case tint::inspector::ResourceBinding::ResourceType::kSampledTexture: - return "SampledTexture"; - case tint::inspector::ResourceBinding::ResourceType::kMultisampledTexture: - return "MultisampledTexture"; - case tint::inspector::ResourceBinding::ResourceType::kWriteOnlyStorageTexture: - return "WriteOnlyStorageTexture"; - case tint::inspector::ResourceBinding::ResourceType::kDepthTexture: - return "DepthTexture"; - case tint::inspector::ResourceBinding::ResourceType::kDepthMultisampledTexture: - return "DepthMultisampledTexture"; - case tint::inspector::ResourceBinding::ResourceType::kExternalTexture: - return "ExternalTexture"; - } - - return "Unknown"; -} - -std::string EntryPointStageToString(tint::inspector::PipelineStage stage) { - switch (stage) { - case tint::inspector::PipelineStage::kVertex: - return "Vertex Shader"; - case tint::inspector::PipelineStage::kFragment: - return "Fragment Shader"; - case tint::inspector::PipelineStage::kCompute: - return "Compute Shader"; - } - return "Unknown"; -} - enum class InputFormat { kUnknown, kWgsl, @@ -432,4 +313,199 @@ void PrintInspectorBindings(tint::inspector::Inspector& inspector) { std::cout << std::string(80, '-') << std::endl; } +std::string EntryPointStageToString(tint::inspector::PipelineStage stage) { + switch (stage) { + case tint::inspector::PipelineStage::kVertex: + return "vertex"; + case tint::inspector::PipelineStage::kFragment: + return "fragment"; + case tint::inspector::PipelineStage::kCompute: + return "compute"; + } + return "Unknown"; +} + +std::string TextureDimensionToString(tint::inspector::ResourceBinding::TextureDimension dim) { + switch (dim) { + case tint::inspector::ResourceBinding::TextureDimension::kNone: + return "None"; + case tint::inspector::ResourceBinding::TextureDimension::k1d: + return "1d"; + case tint::inspector::ResourceBinding::TextureDimension::k2d: + return "2d"; + case tint::inspector::ResourceBinding::TextureDimension::k2dArray: + return "2dArray"; + case tint::inspector::ResourceBinding::TextureDimension::k3d: + return "3d"; + case tint::inspector::ResourceBinding::TextureDimension::kCube: + return "Cube"; + case tint::inspector::ResourceBinding::TextureDimension::kCubeArray: + return "CubeArray"; + } + + return "Unknown"; +} + +std::string SampledKindToString(tint::inspector::ResourceBinding::SampledKind kind) { + switch (kind) { + case tint::inspector::ResourceBinding::SampledKind::kFloat: + return "Float"; + case tint::inspector::ResourceBinding::SampledKind::kUInt: + return "UInt"; + case tint::inspector::ResourceBinding::SampledKind::kSInt: + return "SInt"; + case tint::inspector::ResourceBinding::SampledKind::kUnknown: + break; + } + + return "Unknown"; +} + +std::string TexelFormatToString(tint::inspector::ResourceBinding::TexelFormat format) { + switch (format) { + case tint::inspector::ResourceBinding::TexelFormat::kR32Uint: + return "R32Uint"; + case tint::inspector::ResourceBinding::TexelFormat::kR32Sint: + return "R32Sint"; + case tint::inspector::ResourceBinding::TexelFormat::kR32Float: + return "R32Float"; + case tint::inspector::ResourceBinding::TexelFormat::kBgra8Unorm: + return "Bgra8Unorm"; + case tint::inspector::ResourceBinding::TexelFormat::kRgba8Unorm: + return "Rgba8Unorm"; + case tint::inspector::ResourceBinding::TexelFormat::kRgba8Snorm: + return "Rgba8Snorm"; + case tint::inspector::ResourceBinding::TexelFormat::kRgba8Uint: + return "Rgba8Uint"; + case tint::inspector::ResourceBinding::TexelFormat::kRgba8Sint: + return "Rgba8Sint"; + case tint::inspector::ResourceBinding::TexelFormat::kRg32Uint: + return "Rg32Uint"; + case tint::inspector::ResourceBinding::TexelFormat::kRg32Sint: + return "Rg32Sint"; + case tint::inspector::ResourceBinding::TexelFormat::kRg32Float: + return "Rg32Float"; + case tint::inspector::ResourceBinding::TexelFormat::kRgba16Uint: + return "Rgba16Uint"; + case tint::inspector::ResourceBinding::TexelFormat::kRgba16Sint: + return "Rgba16Sint"; + case tint::inspector::ResourceBinding::TexelFormat::kRgba16Float: + return "Rgba16Float"; + case tint::inspector::ResourceBinding::TexelFormat::kRgba32Uint: + return "Rgba32Uint"; + case tint::inspector::ResourceBinding::TexelFormat::kRgba32Sint: + return "Rgba32Sint"; + case tint::inspector::ResourceBinding::TexelFormat::kRgba32Float: + return "Rgba32Float"; + case tint::inspector::ResourceBinding::TexelFormat::kNone: + return "None"; + } + return "Unknown"; +} + +std::string ResourceTypeToString(tint::inspector::ResourceBinding::ResourceType type) { + switch (type) { + case tint::inspector::ResourceBinding::ResourceType::kUniformBuffer: + return "UniformBuffer"; + case tint::inspector::ResourceBinding::ResourceType::kStorageBuffer: + return "StorageBuffer"; + case tint::inspector::ResourceBinding::ResourceType::kReadOnlyStorageBuffer: + return "ReadOnlyStorageBuffer"; + case tint::inspector::ResourceBinding::ResourceType::kSampler: + return "Sampler"; + case tint::inspector::ResourceBinding::ResourceType::kComparisonSampler: + return "ComparisonSampler"; + case tint::inspector::ResourceBinding::ResourceType::kSampledTexture: + return "SampledTexture"; + case tint::inspector::ResourceBinding::ResourceType::kMultisampledTexture: + return "MultisampledTexture"; + case tint::inspector::ResourceBinding::ResourceType::kWriteOnlyStorageTexture: + return "WriteOnlyStorageTexture"; + case tint::inspector::ResourceBinding::ResourceType::kDepthTexture: + return "DepthTexture"; + case tint::inspector::ResourceBinding::ResourceType::kDepthMultisampledTexture: + return "DepthMultisampledTexture"; + case tint::inspector::ResourceBinding::ResourceType::kExternalTexture: + return "ExternalTexture"; + } + + return "Unknown"; +} + +std::string ComponentTypeToString(tint::inspector::ComponentType type) { + switch (type) { + case tint::inspector::ComponentType::kUnknown: + return "unknown"; + case tint::inspector::ComponentType::kF32: + return "f32"; + case tint::inspector::ComponentType::kU32: + return "u32"; + case tint::inspector::ComponentType::kI32: + return "i32"; + case tint::inspector::ComponentType::kF16: + return "f16"; + } + return "unknown"; +} + +std::string CompositionTypeToString(tint::inspector::CompositionType type) { + switch (type) { + case tint::inspector::CompositionType::kUnknown: + return "unknown"; + case tint::inspector::CompositionType::kScalar: + return "scalar"; + case tint::inspector::CompositionType::kVec2: + return "vec2"; + case tint::inspector::CompositionType::kVec3: + return "vec3"; + case tint::inspector::CompositionType::kVec4: + return "vec4"; + } + return "unknown"; +} + +std::string InterpolationTypeToString(tint::inspector::InterpolationType type) { + switch (type) { + case tint::inspector::InterpolationType::kUnknown: + return "unknown"; + case tint::inspector::InterpolationType::kPerspective: + return "perspective"; + case tint::inspector::InterpolationType::kLinear: + return "linear"; + case tint::inspector::InterpolationType::kFlat: + return "flat"; + } + return "unknown"; +} + +std::string InterpolationSamplingToString(tint::inspector::InterpolationSampling type) { + switch (type) { + case tint::inspector::InterpolationSampling::kUnknown: + return "unknown"; + case tint::inspector::InterpolationSampling::kNone: + return "none"; + case tint::inspector::InterpolationSampling::kCenter: + return "center"; + case tint::inspector::InterpolationSampling::kCentroid: + return "centroid"; + case tint::inspector::InterpolationSampling::kSample: + return "sample"; + } + return "unknown"; +} + +std::string OverrideTypeToString(tint::inspector::Override::Type type) { + switch (type) { + case tint::inspector::Override::Type::kBool: + return "bool"; + case tint::inspector::Override::Type::kFloat32: + return "f32"; + case tint::inspector::Override::Type::kUint32: + return "u32"; + case tint::inspector::Override::Type::kInt32: + return "i32"; + } + return "unknown"; +} + } // namespace tint::cmd diff --git a/src/tint/cmd/helper.h b/src/tint/cmd/helper.h index 8ce0c7b185..3a48ea4ffc 100644 --- a/src/tint/cmd/helper.h +++ b/src/tint/cmd/helper.h @@ -62,6 +62,46 @@ struct LoadProgramOptions { /// @param opts the loading options ProgramInfo LoadProgramInfo(const LoadProgramOptions& opts); +/// @param stage the pipeline stage +/// @returns the string representation +std::string EntryPointStageToString(tint::inspector::PipelineStage stage); + +/// @param dim the dimension +/// @returns the text name +std::string TextureDimensionToString(tint::inspector::ResourceBinding::TextureDimension dim); + +/// @param kind the sample kind +/// @returns the text name +std::string SampledKindToString(tint::inspector::ResourceBinding::SampledKind kind); + +/// @param format the texel format +/// @returns the text name +std::string TexelFormatToString(tint::inspector::ResourceBinding::TexelFormat format); + +/// @param type the resource type +/// @returns the text name +std::string ResourceTypeToString(tint::inspector::ResourceBinding::ResourceType type); + +/// @param type the composition type +/// @return the text name +std::string CompositionTypeToString(tint::inspector::CompositionType type); + +/// @param type the component type +/// @return the text name +std::string ComponentTypeToString(tint::inspector::ComponentType type); + +/// @param type the interpolation sampling type +/// @return the text name +std::string InterpolationSamplingToString(tint::inspector::InterpolationSampling type); + +/// @param type the interpolation type +/// @return the text name +std::string InterpolationTypeToString(tint::inspector::InterpolationType type); + +/// @param type the override type +/// @return the text name +std::string OverrideTypeToString(tint::inspector::Override::Type type); + } // namespace tint::cmd #endif // SRC_TINT_CMD_HELPER_H_ diff --git a/src/tint/cmd/info.cc b/src/tint/cmd/info.cc index 50df90f541..6c244ad50b 100644 --- a/src/tint/cmd/info.cc +++ b/src/tint/cmd/info.cc @@ -36,11 +36,13 @@ struct Options { #endif std::string input_filename; + bool emit_json = false; }; const char kUsage[] = R"(Usage: tint [options] options: + --json -- Emit JSON -h -- This help text )"; @@ -50,6 +52,8 @@ bool ParseArgs(const std::vector& args, Options* opts) { const std::string& arg = args[i]; if (arg == "-h" || arg == "--help") { opts->show_help = true; + } else if (arg == "--json") { + opts->emit_json = true; } else if (!arg.empty()) { if (arg[0] == '-') { std::cerr << "Unrecognized option: " << arg << std::endl; @@ -66,6 +70,246 @@ bool ParseArgs(const std::vector& args, Options* opts) { return true; } +void EmitJson(const tint::Program* program) { + tint::inspector::Inspector inspector(program); + + std::cout << "{" << std::endl; + std::cout << "\"extensions\": [" << std::endl; + + if (!inspector.GetUsedExtensionNames().empty()) { + bool first = true; + for (const auto& name : inspector.GetUsedExtensionNames()) { + if (!first) { + std::cout << ","; + } + first = false; + std::cout << "\"" << name << "\"" << std::endl; + } + } + std::cout << "]," << std::endl; + + std::cout << "\"entry_points\": ["; + + auto stage_var = [&](const tint::inspector::StageVariable& var) { + std::cout << std::endl; + std::cout << "{" << std::endl; + std::cout << "\"name\": \"" << var.name << "\""; + if (var.has_location_attribute) { + std::cout << "," << std::endl; + std::cout << "\"location\": " << var.location_attribute << "," << std::endl; + std::cout << "\"component_type\": \"" + << tint::cmd::ComponentTypeToString(var.component_type) << "\"," << std::endl; + std::cout << "\"composition_type\": \"" + << tint::cmd::CompositionTypeToString(var.composition_type) << "\"," + << std::endl; + std::cout << "\"interpolation\": {" << std::endl; + std::cout << "\"type\": \"" + << tint::cmd::InterpolationTypeToString(var.interpolation_type) << "\"," + << std::endl; + std::cout << "\"sampling\": \"" + << tint::cmd::InterpolationSamplingToString(var.interpolation_sampling) + << "\"" << std::endl; + std::cout << "}" << std::endl; + } + std::cout << std::endl; + std::cout << "}"; + }; + + auto entry_points = inspector.GetEntryPoints(); + bool first = true; + for (auto& entry_point : entry_points) { + if (!first) { + std::cout << ","; + } + first = false; + + std::cout << std::endl; + std::cout << "{" << std::endl; + + std::cout << "\"name\": \"" << entry_point.name << "\"" + << "," << std::endl; + std::cout << "\"stage\": \"" << tint::cmd::EntryPointStageToString(entry_point.stage) + << "\"" + << "," << std::endl; + + if (entry_point.workgroup_size) { + std::cout << "\"workgroup_size\": ["; + std::cout << entry_point.workgroup_size->x << ", " << entry_point.workgroup_size->y + << ", " << entry_point.workgroup_size->z << "]" + << "," << std::endl; + } + + std::cout << "\"input_variables\": ["; + bool input_first = true; + for (const auto& var : entry_point.input_variables) { + if (!input_first) { + std::cout << ","; + } + input_first = false; + stage_var(var); + } + std::cout << std::endl + << "]" + << "," << std::endl; + + std::cout << "\"output_variables\": ["; + bool output_first = true; + for (const auto& var : entry_point.output_variables) { + if (!output_first) { + std::cout << ","; + } + output_first = false; + stage_var(var); + } + std::cout << std::endl + << "]" + << "," << std::endl; + + std::cout << "\"overrides\": ["; + + bool override_first = true; + for (const auto& var : entry_point.overrides) { + if (!override_first) { + std::cout << ","; + } + override_first = false; + + std::cout << std::endl; + std::cout << "{" << std::endl; + std::cout << "\"name\": \"" << var.name << "\"," << std::endl; + std::cout << "\"id\": " << var.id.value << "," << std::endl; + std::cout << "\"type\": \"" << tint::cmd::OverrideTypeToString(var.type) << "\"," + << std::endl; + std::cout << "\"is_initialized\": " << (var.is_initialized ? "true" : "false") << "," + << std::endl; + std::cout << "\"is_id_specified\": " << (var.is_id_specified ? "true" : "false") + << std::endl; + std::cout << "}"; + } + std::cout << std::endl + << "]" + << "," << std::endl; + + std::cout << "\"bindings\": ["; + auto bindings = inspector.GetResourceBindings(entry_point.name); + bool ep_first = true; + for (auto& binding : bindings) { + if (!ep_first) { + std::cout << ","; + } + ep_first = false; + + std::cout << std::endl; + std::cout << "{" << std::endl; + std::cout << "\"binding\": " << binding.binding << "," << std::endl; + std::cout << "\"group\": " << binding.bind_group << "," << std::endl; + std::cout << "\"size\": " << binding.size << "," << std::endl; + std::cout << "\"resource_type\": \"" + << tint::cmd::ResourceTypeToString(binding.resource_type) << "\"," + << std::endl; + std::cout << "\"dimemsions\": \"" << tint::cmd::TextureDimensionToString(binding.dim) + << "\"," << std::endl; + std::cout << "\"sampled_kind\": \"" + << tint::cmd::SampledKindToString(binding.sampled_kind) << "\"," << std::endl; + std::cout << "\"image_format\": \"" + << tint::cmd::TexelFormatToString(binding.image_format) << "\"" << std::endl; + std::cout << "}"; + } + std::cout << std::endl << "]" << std::endl; + std::cout << "}"; + } + std::cout << std::endl << "]," << std::endl; + std::cout << "\"structures\": ["; + + bool struct_first = true; + for (const auto* ty : program->Types()) { + if (!ty->Is()) { + continue; + } + const auto* s = ty->As(); + + if (!struct_first) { + std::cout << ","; + } + struct_first = false; + + std::cout << std::endl; + std::cout << "{" << std::endl; + std::cout << "\"name\": \"" << s->FriendlyName(program->Symbols()) << "\"," << std::endl; + std::cout << "\"align\": " << s->Align() << "," << std::endl; + std::cout << "\"size\": " << s->Size() << "," << std::endl; + std::cout << "\"members\": ["; + for (size_t i = 0; i < s->Members().Length(); ++i) { + auto* const m = s->Members()[i]; + + if (i != 0) { + std::cout << ","; + } + std::cout << std::endl; + + // Output field alignment padding, if any + auto* const prev_member = (i == 0) ? nullptr : s->Members()[i - 1]; + if (prev_member) { + uint32_t padding = m->Offset() - (prev_member->Offset() + prev_member->Size()); + if (padding > 0) { + size_t padding_offset = m->Offset() - padding; + std::cout << "{" << std::endl; + std::cout << "\"name\": \"implicit_padding\"," << std::endl; + std::cout << "\"offset\": " << padding_offset << "," << std::endl; + std::cout << "\"align\": 1," << std::endl; + std::cout << "\"size\": " << padding << std::endl; + std::cout << "}," << std::endl; + } + } + + std::cout << "{" << std::endl; + std::cout << "\"name\": \"" << program->Symbols().NameFor(m->Name()) << "\"," + << std::endl; + std::cout << "\"offset\": " << m->Offset() << "," << std::endl; + std::cout << "\"align\": " << m->Align() << "," << std::endl; + std::cout << "\"size\": " << m->Size() << std::endl; + std::cout << "}"; + } + std::cout << std::endl << "]" << std::endl; + std::cout << "}"; + } + std::cout << std::endl << "]" << std::endl; + std::cout << "}" << std::endl; +} + +void EmitText(const tint::Program* program) { + tint::inspector::Inspector inspector(program); + if (!inspector.GetUsedExtensionNames().empty()) { + std::cout << "Extensions:" << std::endl; + for (const auto& name : inspector.GetUsedExtensionNames()) { + std::cout << "\t" << name << std::endl; + } + } + std::cout << std::endl; + + tint::cmd::PrintInspectorData(inspector); + + bool has_struct = false; + for (const auto* ty : program->Types()) { + if (!ty->Is()) { + continue; + } + has_struct = true; + break; + } + + if (has_struct) { + std::cout << "Structures" << std::endl; + for (const auto* ty : program->Types()) { + if (!ty->Is()) { + continue; + } + const auto* s = ty->As(); + std::cout << s->Layout(program->Symbols()) << std::endl << std::endl; + } + } +} + } // namespace int main(int argc, const char** argv) { @@ -102,36 +346,10 @@ int main(int argc, const char** argv) { source_file = std::move(info.source_file); } - tint::inspector::Inspector inspector(program.get()); - - if (!inspector.GetUsedExtensionNames().empty()) { - std::cout << "Extensions:" << std::endl; - for (const auto& name : inspector.GetUsedExtensionNames()) { - std::cout << "\t" << name << std::endl; - } - } - std::cout << std::endl; - - tint::cmd::PrintInspectorData(inspector); - - bool has_struct = false; - for (const auto* ty : program->Types()) { - if (!ty->Is()) { - continue; - } - has_struct = true; - break; - } - - if (has_struct) { - std::cout << "Structures" << std::endl; - for (const auto* ty : program->Types()) { - if (!ty->Is()) { - continue; - } - const auto* s = ty->As(); - std::cout << s->Layout(program->Symbols()) << std::endl << std::endl; - } + if (options.emit_json) { + EmitJson(program.get()); + } else { + EmitText(program.get()); } return 0;